
在 Laravel 测试中,直接 new 实例无法被 Mock 拦截;必须通过服务容器解析实例,才能使 Mock 生效。本文详解如何在控制器测试中正确注入和替换依赖类(如 Service),确保 getNumber() 等方法返回预期值。
在 laravel 测试中,直接 new 实例无法被 mock 拦截;必须通过服务容器解析实例,才能使 mock 生效。本文详解如何在控制器测试中正确注入和替换依赖类(如 service),确保 `getnumber()` 等方法返回预期值。
问题根源在于:控制器中使用了 new Service() 手动实例化,绕过了 Laravel 的服务容器(Service Container)。而 Laravel 的 Mock 机制(如 partialMock)仅对由容器解析的对象生效——Mock 是通过容器绑定覆盖实现的,而非运行时拦截任意对象。
✅ 正确做法:让容器管理依赖
首先,修改控制器,通过构造函数注入 Service 实例,交由容器自动解析:
// app/Http/Controllers/ServiceController.php
class ServiceController extends Controller
{
protected $service;
public function __construct(Service $service)
{
$this->service = $service;
}
public function fetch(Request $request)
{
return [$this->service->getNumber()];
}
}⚠️ 注意:Laravel 默认支持控制器构造函数自动注入(需确保未禁用自动解析),且 Service 类无需显式绑定(若为普通 PHP 类,可按需在 AppServiceProvider::boot() 中绑定)。
? 测试代码:Mock 容器绑定
在测试中,使用 mock()(非 partialMock)替代原始绑定,并确保在请求前完成注册:
// tests/Feature/ControllerTest.php
use Illuminate\Foundation\Testing\RefreshDatabase;
class ControllerTest extends TestCase
{
use RefreshDatabase;
public function testFetchGetsNumber()
{
// Mock Service 类并绑定到容器(覆盖默认解析)
$this->mock(Service::class, function ($mock) {
$mock->shouldReceive('getNumber')->andReturn(200);
});
$response = $this->post('/api/controller');
$response->assertJson([200]); // 断言返回 [200]
}
}✅ 关键点说明:
- mock() 是 Laravel 测试助手提供的便捷方法,等价于 app()->bind(Service::class, fn() => $mock);
- partialMock() 适用于需要部分方法真实调用的场景,但本例只需完全替换 getNumber(),mock() 更简洁;
- 绑定必须在 HTTP 请求发起之前执行,否则容器已缓存原始实例。
? 替代方案:手动 resolve + 临时绑定(不推荐)
若因历史原因无法修改控制器构造函数,可强制重绑定并触发重新解析,但强烈不建议:
public function testFetchGetsNumberWithResolve()
{
$mock = Mockery::mock(Service::class);
$mock->shouldReceive('getNumber')->andReturn(200);
// 临时替换容器绑定(注意:需清理 Mockery,避免污染)
$this->app->instance(Service::class, $mock);
$response = $this->post('/api/controller');
$response->assertJson([200]);
}⚠️ 风险提示:$this->app->instance() 是硬替换,若未在 tearDown() 中恢复或清理,可能影响后续测试。应优先采用依赖注入方式。
? 总结
| 方式 | 是否推荐 | 原因 |
|---|---|---|
| new Service() + partialMock | ❌ | Mock 对象未被使用,无效 |
| 构造函数注入 + mock() | ✅ | 符合 Laravel 依赖注入规范,Mock 自动生效 |
| app()->bind() / instance() | ⚠️ | 可行但易出错,破坏测试隔离性 |
遵循“依赖注入优于手动实例化”原则,不仅能解决 Mock 失效问题,还能提升代码可测试性与可维护性。记住:Laravel 的 Mock 生效的前提,是对象必须由容器创建。


















