laravel 6测试中模拟服务类的核心是通过容器注入mockery实例以隔离外部依赖。推荐顺序为:①用$this->mock()快速绑定并定义行为;②用app()->instance()手动注入精细控制;③对facade调用使用shouldreceive();禁用new实例化或未绑定接口的make()。

Laravel 6 的服务容器在测试中模拟服务类,核心是让容器返回你控制的模拟实例,而不是真实对象。这能切断数据库、HTTP 请求、支付网关等外部依赖,使测试快、稳、可重复。
下面几种方式按推荐顺序排列,适用于 Laravel 6(注意:Laravel 6 默认使用 Mockery,且 TestCase 已内置清理机制):
使用 $this->mock() 快速替换绑定类
这是最简洁、最符合 Laravel 6 风格的方式。它自动调用 app()->instance() 注入模拟对象,并支持链式期望定义。
use App\Services\PaymentService;
public function test_payment_fails_gracefully()
{
$this->mock(PaymentService::class, function ($mock) {
$mock->shouldReceive('charge')
->with(99.99)
->andThrow(new \Exception('Network timeout'));
});
$response = $this->post('/api/checkout', ['amount' => 99.99]);
$response->assertStatus(500);
}
✅ 优点:一行绑定 + 行为定义,无需手动管理 Mockery 实例生命周期
⚠️ 注意:被测代码必须通过容器获取该服务(如构造注入或 app(PaymentService::class)),不能 new PaymentService(),否则 mock 失效。
使用 app()->instance() 手动注入 Mockery 实例
适合需要精细控制(如部分方法 mock、其他方法保留原逻辑)或兼容老写法的场景。
use Mockery;
use App\Services\AnalyticsService;
public function test_analytics_tracks_user_action()
{
$mock = Mockery::mock(AnalyticsService::class);
$mock->shouldReceive('track')->once()->with('login', ['user_id' => 123]);
$this->app->instance(AnalyticsService::class, $mock);
// 触发被测逻辑(例如登录控制器)
$this->actingAs(User::factory()->create())
->get('/dashboard');
// Mockery 自动验证(TestCase 已注册 tearDownMockery)
}
✅ 优点:完全掌控模拟对象,支持 spy、partialMock 等高级用法
⚠️ 注意:确保在 setUp() 或测试方法开头执行;若服务是 singleton 绑定,建议先 app()->forgetInstance(AnalyticsService::class) 避免状态残留。
使用 Facade 模拟(针对静态门面调用)
如果你的服务被封装成自定义 Facade(如 Analytics::track()),就不能用 instance(),得用 shouldReceive():
use App\Facades\Analytics;
public function test_facade_call_is_mocked()
{
Analytics::shouldReceive('track')
->with('signup', ['source' => 'web'])
->andReturn(true);
$user = User::factory()->create();
app(UserService::class)->onBoard($user); // 内部调用 Analytics::track()
}
✅ 优点:适配门面调用模式,语法直观
⚠️ 注意:Facade 必须已注册(extend 或 getFacadeAccessor 正确),且不能和 instance() 混用。
不要直接 new 类,也不要跳过容器
常见错误写法(会导致 mock 完全失效):
// ❌ 错误:绕过容器,Mockery 拦不住
$service = new PaymentService(); // → 调用真实第三方 API!
// ❌ 错误:未绑定接口,容器不知道怎么解析
$this->app->make('App\Contracts\PaymentGateway'); // 报错:Unresolvable dependency
✅ 正确做法:
- 在
AppServiceProvider中绑定接口实现(如bind(PaymentGateway::class, StripeGateway::class)) - 测试中用
$this->mock(PaymentGateway::class)替换即可,保持契约一致
不复杂但容易忽略











