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

PHPUnit:初始化设置后重新定义Mock的行为

在PHPUnit中重新定义已初始化Mock的行为

嘿,刚好我之前也遇到过一模一样的场景——用辅助函数封装Mock的基础配置,然后在单个测试里按需调整行为,完全没问题!下面给你几种实用的方法:

1. 直接覆盖或新增方法预期

一旦你通过mockTransport()拿到Mock实例,就可以像初始化时那样,再次调用expects()->method()来修改它的行为。PHPUnit允许你对同一个Mock对象多次设置方法预期:后续的配置会覆盖之前针对相同方法+相同匹配条件的预期,不同的方法或者不同参数匹配的预期则可以共存。

举个实际的例子:

// 先获取基础配置好的Mock
$mockClient = $this->mockTransport();

// 重新定义get方法的行为:针对特定URL返回成功响应
$mockClient->expects($this->once())
           ->method('get')
           ->with('/api/user/123')
           ->willReturn(new Response(200, [], '{"id":123,"name":"Gordon"}'));

// 甚至可以新增其他方法的预期,比如post
$mockClient->expects($this->once())
           ->method('post')
           ->with('/api/user', ['name' => 'New User'])
           ->willReturn(new Response(201, [], '{"id":456}'));

2. 重置Mock的所有预期

如果你想完全清空之前的基础配置,从零开始定义行为,可以调用resetMock()方法清除所有已设置的预期,然后重新配置:

$mockClient = $this->mockTransport();
$mockClient->resetMock(); // 清除辅助函数里的所有默认预期

// 现在可以设置全新的行为
$mockClient->expects($this->exactly(2))
           ->method('delete')
           ->willReturn(new Response(204));

3. 完整测试类示例

为了更清晰,给你看一个包含辅助函数和测试方法的完整结构:

use PHPUnit\Framework\TestCase;

class YourClientTest extends TestCase
{
    private function mockTransport(): HTTPClient
    {
        $mock = $this->getMockBuilder(HTTPClient::class)
                     ->disableOriginalConstructor()
                     ->getMock();
        
        // 基础默认配置:任意调用get都返回空200响应
        $mock->expects($this->any())
             ->method('get')
             ->willReturn(new Response(200, [], ''));
        
        return $mock;
    }

    public function testFetchExistingUser()
    {
        $mockClient = $this->mockTransport();
        
        // 覆盖get方法的行为,针对用户ID返回具体数据
        $mockClient->expects($this->once())
                   ->method('get')
                   ->with('/api/user/123')
                   ->willReturn(new Response(200, [], '{"id":123,"name":"Gordon"}'));

        $client = new YourClient($mockClient);
        $user = $client->getUser(123);
        
        $this->assertEquals('Gordon', $user['name']);
    }

    public function testFetchNonExistentUserThrowsError()
    {
        $mockClient = $this->mockTransport();
        
        // 重新定义get方法返回404
        $mockClient->expects($this->once())
                   ->method('get')
                   ->with('/api/user/999')
                   ->willReturn(new Response(404, [], ''));

        $client = new YourClient($mockClient);
        $this->expectException(UserNotFoundException::class);
        
        $client->getUser(999);
    }
}

关键注意点

  • 如果你在辅助函数里用了$this->any(),后续设置更严格的调用次数(比如once())会覆盖之前的预期,也就是说该方法必须符合新的调用次数要求。
  • 确保每个测试方法都调用mockTransport()获取新的Mock实例,避免测试之间的状态污染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:13:20