Laravel 6 单元测试中 Mock 生效的关键是替换服务容器绑定而非对象实例;必须通过构造/方法注入使容器解析依赖,手动 new 的对象无法被 Mock 拦截;使用 $this->mock() 或门面 fake() 可便捷实现模拟。

在 Laravel 6 的单元测试中,要让 Mock 生效,关键不是“替换对象”,而是“替换容器绑定”——因为 Laravel 只对经服务容器解析的实例应用 Mock。手动 new 出来的对象完全绕过容器,Mock 无法拦截。
必须通过容器解析依赖
控制器或服务中若写 $service = new Service(),无论你怎么 mock,它都用不到模拟实例。正确做法是使用构造函数注入或方法注入,让容器负责实例化:
- 控制器中声明
public function __construct(Service $service),Laravel 自动注入 - 确保该类未被禁用自动解析(默认开启)
- 普通类无需提前绑定;如需自定义行为(比如单例、带参数构造),可在
AppServiceProvider::boot()中用$this->app->singleton()或bind()
测试中用 $this->mock() 注册模拟实例
Laravel 测试基类提供便捷方法,一行代码完成 mock 创建 + 容器绑定:
$this->mock(Service::class, function ($mock) { $mock->shouldReceive('getNumber')->andReturn(42); });- 这等价于先
Mockery::mock(),再$this->instance(Service::class, $mock) - 调用必须在发起 HTTP 请求或执行被测逻辑之前完成
对门面(Facades)直接 fake
如果业务代码用了 Cache::get()、Mail::send() 这类门面,不需要 mock 类本身:
-
Cache::fake()清空所有缓存操作并允许断言 -
Mail::fake()拦截发送,后续可用Mail::assertSent() - 不支持
fake()的门面(如自定义门面),改用SomeFacade::shouldReceive('method')
特殊场景:final 类或带参解析
遇到 final 类无法 mock?需额外扩展:
- 安装
dg/bypass-finals,并在phpunit.xml中注册 hook - mock 时写
$this->mock('overload:' . FinalClass::class) - 若类需带参数构造(如
app(MyClass::class, [$data])),用app()->bind(MyClass::class, fn() => $spy)强制所有解析返回同一实例


















