
在 laravel 测试中,直接 new 实例化类会导致 mock 失效;必须通过服务容器解析实例,才能使 mocked 方法生效。本文详解如何在控制器测试中正确 mock 依赖类的方法,并提供可运行的修复代码与关键注意事项。
在 laravel 测试中,直接 new 实例化类会导致 mock 失效;必须通过服务容器解析实例,才能使 mocked 方法生效。本文详解如何在控制器测试中正确 mock 依赖类的方法,并提供可运行的修复代码与关键注意事项。
问题根源在于:测试中 new Service() 绕过了 Laravel 的服务容器,因此即使你对 Service::class 创建了 partialMock,实际控制器里调用的仍是原始未 mock 的实例——mock 对象从未被注入或使用。
✅ 正确做法是 让控制器从容器中获取 Service 实例,而非手动 new。这需要两步改造:
1. 修改控制器:依赖注入替代手动实例化
use App\Models\Service;
class ServiceController extends Controller
{
public function fetch(Request $request, Service $service) // ✅ 通过参数注入
{
return [$service->getNumber()];
}
}
? Laravel 自动将类型提示的 Service 通过容器解析。若 Service 是 Eloquent 模型(非绑定到容器的服务),需先显式绑定(见下文)。
2. 在测试中绑定 Mock 到容器
class ControllerTest extends TestCase
{
public function testFetchGetsNumber()
{
// ✅ 使用容器绑定 mock 实例(Laravel 8+ 推荐方式)
$mock = Mockery::mock(Service::class);
$mock->shouldReceive('getNumber')->andReturn(200);
$this->app->instance(Service::class, $mock); // 绑定到容器
$response = $this->postJson('/api/controller'); // 建议用 postJson 更规范
$response->assertJson([200]); // 断言返回 [200]
}
}
⚠️ 注意事项:
- 若 Service 是 Eloquent 模型(如继承 Model),默认不会自动绑定到容器。你必须显式调用 $this->app->instance(...) 或在 AppServiceProvider 中绑定(如 bind(Service::class, fn() => new Service()))。但测试中推荐 instance() 直接替换。
- partialMock() 在此场景不适用——它用于 mock 部分方法但仍保留原实例逻辑,而你需要的是完全替换容器中的实例。
- 不要使用 $this->partialMock(Service::class, ...),因为它返回的是 mock 对象,但未注册到容器,控制器仍会 new 出原始实例。
- 确保路由 /api/controller 已正确定义并指向该控制器方法(例如 Route::post('/api/controller', [ServiceController::class, 'fetch']))。
? 总结:Laravel 的 Mock 生效前提是“被测试代码从容器获取对象”。无论是控制器方法参数注入、构造函数注入,还是 resolve()/app() 辅助函数,本质都是利用容器的依赖解析机制。脱离容器的手动 new 永远无法触发 Mock 行为。











