为什么使用 Mock
在编写测试时,有些处理是我们实际上并不希望真正执行的,比如发送邮件、读写缓存、调用外部 API 等。
用「装作真实存在的假对象」替换这些处理,就叫做 Mock(模拟)。
使用 Mock 的好处:
- 测试更快 — 不执行外部服务或耗时处理,测试很快完成
- 测试更稳定 — 不受外部服务状态影响,结果始终一致
- 测试范围更清晰 — 可以只验证某一个类或方法
Laravel 为事件、任务、门面等提供了即用型的 Mock 辅助方法。
内部使用 Mockery,无需复杂设置即可使用。
Mock 对象
要 mock 通过服务容器注入的对象,可以将 mock 实例绑定到容器上。
这样容器就会使用 mock 实例,而不是新建对象。
mock() 方法
使用 Laravel 测试基类提供的 mock() 方法,可以更简洁地写出等效逻辑。
partialMock() 方法
如果只想 mock 对象的部分方法,可以使用 partialMock()。
未被 mock 的方法会照常执行。
spy() 方法
Spy(间谍对象)与 mock 类似,不同之处在于它是在代码执行之后再验证交互。
Mock 在事前设置「这个方法应当被调用」,而 spy 是在事后检查「这个方法是否被调用了」。
Mock 门面
与普通的静态方法调用不同,门面是可以被 mock 的。
它拥有与依赖注入相当的可测试性,同时保留了简洁的书写方式。
以下面这个使用缓存的控制器为例:
要 mock Cache 门面的 get 方法,请使用 expects()。
请勿 mock Request 门面。作为替代,请将输入值传给 get、post 等 HTTP 测试方法。同样,请不要 mock Config 门面,而是在测试内调用 Config::set()。
门面 Spy
要用 spy 监视门面,请调用对应门面的 spy() 方法。
Spy 适合在代码执行后再验证交互的场景。
操作时间
在测试与时间相关的逻辑时,能够修改 now() 或 Carbon::now() 返回的时刻非常方便。
Laravel 的功能测试基类提供了操作时间的辅助方法。
travel() — 移动时间
使用闭包进行时间移动
向时间移动方法传入闭包时,会在指定时刻将时间冻结并执行闭包,闭包执行完毕后再恢复到原来的时间。
freezeTime() — 冻结时间
freezeTime() 会冻结当前时间。freezeSecond() 则会把时间冻结到当前秒的开头。
实用示例:非活跃线程被锁定
时间操作非常适合测试类似论坛「一段时间无操作后帖子被锁定」这样的功能。
travel() 等时间操作方法只能在功能测试(继承 Tests\TestCase 的类)中使用。在 PHPUnit 的基类 TestCase 中无法使用。
方法一览
Mock 相关
时间操作相关