为什么要用Mock:解决依赖、加速测试、隔离环境、提升可靠性

做Android测试久了,你会发现一个很现实的问题:写一个单元测试,有时候比写业务代码还痛苦。我不是在开玩笑。我刚入行那会儿,写一个网络请求的测试,要先配测试服务器、准备数据库、还要确保网络是通的……折腾半天,最后测试还是挂了——因为服务器超时。后来我意识到,不解决外部依赖的问题,测试就是一把双刃剑。这,就是我们为什么要用Mock。

Mock的核心思想很朴素:用假的代替真的,让测试只关注你要测的那个单元。

解决依赖:把“外部的变数”变成“可控的桩”

你的ViewModel依赖Repository,Repository依赖Retrofit,Retrofit依赖网络接口。任何一个环节出问题,你的测试都会报错——哪怕是网络断了。我在项目中遇到过类似的场景:一次数据库迁移导致ORM映射出错,结果整个模块的测试全部爆红,可那跟我要测的业务逻辑半毛钱关系没有。

用Mock可以做到:把依赖的模块替换成一个“冒牌货”。你告诉它:“当别人调用你的getUser()时,你就返回这个假数据。” 这样,真实的网络、数据库、文件系统都不用参与,测试只跑你关心的代码。

// 不使用Mock:必须启动Activity,依赖NetworkModule
val api = Retrofit.Builder().baseUrl("https://real-server.com").build().create(Api::class.java)
val repo = UserRepository(api)
val result = repo.getUser()  // 真的发起网络请求

// 使用Mock:只需一个fake对象
val mockApi = mock(Api::class.java)
`when`(mockApi.getUser()).thenReturn(User("test", "123"))
val repo = UserRepository(mockApi)
val result = repo.getUser()  // 瞬间返回,不依赖任何外部

我建议你养成习惯:所有跨进程、跨网络、跨模块的接口,都设计成可Mock的。界面层用Dagger/Hilt注入,测试时替换为Mock实现。这样以后改代码,测试不用跟着重构。

加速测试:从几十秒到几毫秒

每个单元测试如果都去读写数据库、调用网络,跑一遍全量测试可能要半小时。我记得有一次,团队把集成测试改成Mock单元测试后,CI流水线从40分钟降到了3分钟——这相当于一天能多跑十几次回归。说白了,Mock帮你把“慢操作”变成“内存里的函数调用”。

测试类型典型执行时间依赖情况
带真实网络请求1~5 秒/个网络可用、服务器正常
带本地数据库100~500 毫秒/个数据库初始化、文件IO
纯Mock测试1~10 毫秒/个仅内存

你想想看,当你的项目有1000个测试,3毫秒一个和3秒一个,差距是多少?不是3倍,是1000倍。

隔离环境:每人每次跑的都是“同一个世界”

测试环境不稳定是最坑的:测试数据库突然被清空、某个依赖服务正在灰度发布、路由器断流……这些都可能导致你的测试无故失败。我曾经遇到过一次,因为测试服务器上另一个团队提交的代码改了某个接口的字段类型,导致我所有的CRUD测试全挂,可我的App根本没改过。

Mock在测试环境中创建了一个“沙盒”:没有网络波动,没有时序竞争,没有脏数据。每次运行测试,都是相同的初始状态。这样你就能放心地说:“我的测试通过了,那代码就没问题。”

注意:Mock环境可能和真实环境存在差异。比如Mock返回永远不会超时,但真实网络会。我的建议是:核心业务逻辑用Mock做单元测试,再配少量集成测试覆盖真实网络/数据库场景。两者不冲突,互补。

提升可靠性:让测试结果“说人话”

一个测试如果偶尔pass、偶尔fail,那它基本等于没用。真实依赖的不确定性会让测试结果忽绿忽红,大家渐渐就不信任测试了。用Mock之后,测试变得“确定性”:只要代码不变,结果永远一样。你可以放心地根据测试结果决定是否上线。

我之前接手的项目,集成测试失败率高达30%,团队默认“失败了重新跑一遍就好”。后来我们把大部分依赖Mock化,失败率降到1%以下。从那以后,所有人都不敢再跳过测试了——因为它真的靠谱。

一张图看透Mock的核心价值

下面这张图帮你梳理Mock的四个核心价值。它们不是孤立存在的,而是层层递进:先解决依赖,才能跑得快;跑得快才能频繁执行;频繁执行才能暴露问题;暴露问题才能提升可靠性。

Mock的核心价值 为什么用Mock 解决依赖 加速测试 隔离环境 提升可靠性 外部模块替换为假对象 测试与外界完全解耦 毫秒级响应 CI流水线时间大幅缩减 沙盒环境 无网络波动 每次运行相同初始状态 测试结果确定性强 可放心作为发布依据 四种价值环环相扣,推动测试左移、质量内建

总结:为什么你值得在项目里推广Mock

说到底,Mock不是银弹,但它解决了一个根本问题:让测试回归“测试代码”本身。我做了这么多年Android,见过太多项目因为测试太难维护而放弃测试,最后靠人肉回归。如果你能把这四个价值讲给团队听,再配上一两个实际案例——比如我提到的CI时间从40分钟降到3分钟——相信我,没人会拒绝试试看。

一句话记牢: Mock不是偷懒,是让每一次测试都更有价值。