如何为调用含公共常量与构造函数的第三方类编写单元测试
针对第三方类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
相关产品推荐
相关产品推荐

