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

如何使用PHPUnit Mock Laravel Eloquent模型并设置自定义属性

问题原因

你使用PHPUnit默认创建的是完整Mock对象,所有原生方法都会被替换为空桩(默认返回null):

  • Eloquent模型的属性赋值($user->id = $id)本质是调用魔术方法__set(),该方法已经被Mock覆盖,不会执行原生的属性写入逻辑
  • 同理setAttribute()方法也被Mock为空,赋值后不会存入模型的属性数组,自然读取时返回null

可行解决方案

方案1:为属性访问方法打桩(最简便,适合仅需设置少量属性的场景)

Eloquent读取属性最终都会走getAttribute()方法,直接为该方法打桩指定返回值即可:

/**
 * @param int $id
 * @return User|\PHPUnit\Framework\MockObject\MockObject
 */
protected function createMockUser(int $id): \PHPUnit\Framework\MockObject\MockObject|User
{
    $user = $this
        ->getMockBuilder(User::class)
        ->disableOriginalConstructor()
        ->getMock();
    // 为id属性的访问打桩
    $user->method('getAttribute')
        ->with('id')
        ->willReturn($id);

    return $user;
}

如果需要支持多属性返回,可以调整桩逻辑:

$attributes = ['id' => $id, 'name' => 'test'];
$user->method('getAttribute')
    ->willReturnCallback(fn($key) => $attributes[$key] ?? null);

方案2:使用部分Mock,保留原生属性操作逻辑

如果你需要保留模型原生的属性读写逻辑,仅Mock你需要自定义的业务方法(如save、delete等),可以通过onlyMethods指定要替换的方法:

protected function createMockUser(int $id): \PHPUnit\Framework\MockObject\MockObject|User
{
    $user = $this
        ->getMockBuilder(User::class)
        // 仅替换你需要自定义逻辑的方法,属性相关方法保留原生实现
        ->onlyMethods(['save', 'delete'])
        ->disableOriginalConstructor()
        ->getMock();
    // 此时可以正常使用原生方法赋值
    $user->id = $id;

    return $user;
}

方案3:直接实例化模型(无特殊隔离需求时首选)

如果你的测试不需要完全隔离模型的基础逻辑,完全可以跳过PHPUnit Mock,直接实例化模型设置属性,代码更简洁:

protected function createMockUser(int $id): User
{
    return User::make(['id' => $id]);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 17:24:03