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

Laravel模型Mocking测试问题:PHPUnit中update方法未被调用

解决PHPUnit中Mock Post模型update方法未被调用的问题

问题根源

你当前的测试代码存在核心逻辑错误:

  • 你Mock的$postMock对象仅定义了findOrFail的行为,但findOrFail返回的是真实的Post模型实例(由Post::factory()->make()生成),而非你Mock的对象。
  • 后续调用$post->update()时,执行的是真实模型的update方法,完全没用到你Mock的$postMock的预期,所以shouldReceive('update')永远不会被触发。
  • 此外,make()生成的模型是内存对象(未持久化到数据库),真实的update方法会返回false/null,且不会修改模型属性,导致日志中模型未更新。

解决方案

方案一:Mock实例+静态方法重载(基于Mockery)

让findOrFail返回Mock的Post实例,确保update调用触发Mock预期:

public function test_can_update_a_post(): void
{
    $mockedTitle = 'Updated Title';
    $mockedSlug = Str::slug($mockedTitle);
    $mockedPostData = ['title' => $mockedTitle, 'slug' => $mockedSlug];
    
    // 准备更新后的模型数据
    $updatedPostModel = Post::factory()->make($mockedPostData);

    // Mock单个Post实例,定义update行为
    $postInstanceMock = Mockery::mock(Post::class);
    $postInstanceMock->shouldReceive('update')
        ->once()
        ->with($mockedPostData)
        ->andReturn($updatedPostModel);
    // 设置Mock实例的初始属性,模拟未更新状态
    $postInstanceMock->title = 'Otho Pfeffer';
    $postInstanceMock->slug = 'otho-pfeffer';

    // 重载Post类,Mock静态方法findOrFail,返回上面的实例Mock
    $postClassMock = Mockery::mock('overload:' . Post::class);
    $postClassMock->shouldReceive('findOrFail')
        ->once()
        ->with(1)
        ->andReturn($postInstanceMock);

    $postService = new AdminPostService($postClassMock);
    $post = $postService->findOrFail(1);
    
    $result = $post->update($mockedPostData);
    
    // 断言更新结果符合预期
    $this->assertEquals($updatedPostModel->title, $result->title);
    $this->assertEquals($updatedPostModel->slug, $result->slug);
}

方案二:使用Laravel内置模型模拟(更简洁)

利用Laravel的Post::fake()功能,无需手动编写Mockery代码:

public function test_can_update_a_post(): void
{
    $mockedTitle = 'Updated Title';
    $mockedSlug = Str::slug($mockedTitle);
    $mockedPostData = ['title' => $mockedTitle, 'slug' => $mockedSlug];

    // 模拟Post模型的数据库操作
    Post::fake();
    // 创建初始帖子并写入模拟数据库
    $initialPost = Post::factory()->create([
        'title' => 'Otho Pfeffer',
        'slug' => 'otho-pfeffer'
    ]);

    $postService = new AdminPostService(new Post());
    $post = $postService->findOrFail($initialPost->id);
    
    $post->update($mockedPostData);
    
    // 断言模型属性已更新
    $this->assertEquals($mockedTitle, $post->title);
    $this->assertEquals($mockedSlug, $post->slug);
    // 断言模拟数据库中的记录已更新
    Post::assertExists([
        'id' => $initialPost->id,
        'title' => $mockedTitle,
        'slug' => $mockedSlug
    ]);
}

内容的提问来源于stack exchange,提问作者Mr. Kenneth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:24:55