You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony单元测试报错:方法期望调用1次实际调用0次排查

问题原因

测试不通过是两个逻辑错误导致Mock完全没生效:

  • 执行顺序反了:你先调用$this->client->request()发起了接口请求,整个控制器逻辑都跑完了才创建Mock对象,此时真实仓储的删除方法早就执行完了,Mock根本碰不到调用逻辑。
  • 没有把Mock注入容器:你创建的Mock只是测试方法里的局部变量,从来没有替换掉框架服务容器里的真实仓储实例,控制器里调用$this->postRepository()拿到的还是原来的真实对象,和你创建的Mock没有任何关联,自然统计不到调用次数。
修复后的测试代码

把Mock创建、容器替换的步骤移到发起请求之前,确保请求进来时控制器拿到的就是你配置了期望的Mock对象:

public function testDelete()
{
    $postId = PostDataSeeder::POST_ID;

    // 先创建Mock、配置调用期望,必须在发请求前完成
    $postRepositoryMock = $this->createMock(PostRepository::class);
    $postRepositoryMock
        ->expects($this->once())
        ->method('deletePost')
        ->with($postId);

    $commentsRepositoryMock = $this->createMock(CommentsRepository::class);
    $commentsRepositoryMock
        ->expects($this->once())
        ->method('deleteComments')
        ->with($postId);

    // 将Mock替换到服务容器,覆盖原来的真实实例
    // 以下是Symfony框架的容器替换写法,如果你用其他框架,替换对应容器内的服务即可
    $this->client->getContainer()->set(PostRepository::class, $postRepositoryMock);
    $this->client->getContainer()->set(CommentsRepository::class, $commentsRepositoryMock);

    // 构造路由发起请求
    $route = $this->getUrl('api_delete_post', [
        '_format' => 'json',
        'postId' => $postId
    ]);

    $this->client->request('DELETE', $route, [], [], [
        'CONTENT_TYPE' => 'application/json',
    ]);

    // 可选:补充断言响应状态码符合预期
    $this->assertResponseStatusCodeSame(204);
}
额外说明
  • 如果你的postRepository()、commentsRepository()方法是直接new实例而不是从容器取,需要先调整控制器的获取逻辑,统一从服务容器拉取依赖,否则Mock永远无法接管真实调用。
  • 不需要在测试里提前调用findOne查真实文章实体,你要验证的只是删除方法被正确ID调用,直接用种子里定义的常量ID即可,多余的数据库查询会拖慢单元测试速度,还可能引入数据依赖导致的偶发报错。

内容的提问来源于stack exchange,提问作者Zak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 00:06:24