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

如何为调用含公共常量与构造函数的第三方类编写单元测试

针对第三方类ResponseMessage的单元测试方案

一、Mock第三方类的测试方案(使用Mockery)

由于myClass中直接通过new实例化ResponseMessage,需要使用Mockery的别名Mock(alias:)来全局替换这个第三方类,从而验证构造参数和方法调用行为。

正确的测试代码示例

use Mockery;
use PHPUnit\Framework\TestCase;

class testMyClass extends TestCase
{
    protected function tearDown(): void
    {
        Mockery::close(); // 清理Mockery实例,避免内存泄漏
    }

    public function testProcessWithInputOne()
    {
        // 创建ResponseMessage的别名Mock,覆盖全局类
        $mockResponse = Mockery::mock('alias:ResponseMessage');

        // 验证第一次构造调用:参数为'The input is 1'和STATUS_SUCCESS
        $mockResponse->shouldReceive('__construct')
            ->with('The input is 1', ResponseMessage::STATUS_SUCCESS)
            ->once()
            ->andReturnSelf(); // 让构造函数返回Mock实例本身

        // 验证第一次sendMessage调用:默认参数false
        $mockResponse->shouldReceive('sendMessage')
            ->with(false)
            ->once();

        // 验证第二次构造调用:参数为'The comparison is triggered'和STATUS_SUCCESS
        $mockResponse->shouldReceive('__construct')
            ->with('The comparison is triggered', ResponseMessage::STATUS_SUCCESS)
            ->once()
            ->andReturnSelf();

        // 验证第二次sendMessage调用:参数true
        $mockResponse->shouldReceive('sendMessage')
            ->with(true)
            ->once();

        // 执行测试逻辑
        $myClass = new myClass();
        $myClass->handle(1);
    }
}

关键说明

  • 无需Mock公共常量:ResponseMessage::STATUS_SUCCESS是公共静态常量,直接使用原类的值即可,不需要额外Mock。
  • andReturnSelf()确保构造函数返回Mock实例,这样后续的sendMessage调用会被Mock捕获。
  • 测试结束后必须调用Mockery::close()清理资源。

二、无需Mock的测试方案(测试行为而非实现)

如果不想依赖Mock库,可以通过测试替身类来捕获ResponseMessage的调用行为,适合原类构造逻辑简单的场景。

测试替身类方案示例

use PHPUnit\Framework\TestCase;

// 定义测试替身类,继承原ResponseMessage并重写核心方法
class ResponseMessage extends \OriginalNamespace\ResponseMessage
{
    public static $sendCalls = [];
    public static $constructArgs = [];

    public function __construct($responseMessage, $responseCode)
    {
        // 记录构造参数
        self::$constructArgs[] = [$responseMessage, $responseCode];
        // 若原构造逻辑复杂,可跳过parent::__construct()避免干扰测试
        // parent::__construct($responseMessage, $responseCode);
    }

    public function sendMessage($sendToAdmin = false)
    {
        // 记录sendMessage的调用参数
        self::$sendCalls[] = $sendToAdmin;
    }
}

class testMyClass extends TestCase
{
    protected function setUp(): void
    {
        // 每次测试前重置记录数组
        ResponseMessage::$sendCalls = [];
        ResponseMessage::$constructArgs = [];
    }

    public function testProcessWithInputOne()
    {
        $myClass = new myClass();
        $myClass->handle(1);

        // 验证构造参数是否符合预期
        $expectedConstructArgs = [
            ['The input is 1', ResponseMessage::STATUS_SUCCESS],
            ['The comparison is triggered', ResponseMessage::STATUS_SUCCESS]
        ];
        $this->assertEquals($expectedConstructArgs, ResponseMessage::$constructArgs);

        // 验证sendMessage的调用参数顺序
        $this->assertEquals([false, true], ResponseMessage::$sendCalls);
    }
}

关键说明

  • 替换\OriginalNamespace\为ResponseMessage实际所在的命名空间。
  • 若原构造函数有复杂初始化逻辑,可跳过父类构造,只记录参数即可,避免测试受第三方类内部逻辑影响。
  • 这种方案直接测试业务行为的副作用,更贴近真实运行场景,无需Mock依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 17:34:55