如何用PHPUnit正确测试Resolver类的回调与格式化方法?
好的,我来一步步帮你梳理怎么用PHPUnit测试这个逻辑~
测试Resolver::initiate()方法的Closure处理逻辑
我们需要聚焦四个核心验证点,确保代码逻辑完全符合预期:
- 当
$table->action是Closure实例时,这个闭包确实被执行了 - 闭包被调用时,正确传入了
$table->params参数 - 闭包的返回值被准确传递给
resolveByType()方法 - 最终返回的是
resolveByType()处理后的格式化结果
接下来我会给出具体的测试代码示例,搭配详细解释:
1. 准备测试依赖与模拟对象
首先我们需要模拟$table对象(假设它是某个业务类的实例),用来控制action和params属性。这里用匿名类实现会更简单直观:
// 创建一个模拟的Table对象,包含可修改的action和params属性 $mockTable = new class { public $action; public $params; };
假设我们要测试的Resolver类基础结构如下(方便你对应测试逻辑):
class Resolver { public function initiate($table) { if ($table->action instanceof \Closure) { $result = ($table->action)($table->params); return $this->resolveByType($result); } // 其他非Closure场景的逻辑... } protected function resolveByType($data) { // 示例格式化逻辑:给原始数据添加前缀 return "formatted: " . $data; } }
2. 编写基础测试用例
这个测试会覆盖Closure执行、参数传递、结果格式化的完整流程:
use PHPUnit\Framework\TestCase; class ResolverTest extends TestCase { public function testInitiateExecutesClosureWithParamsAndReturnsFormattedResult() { // 1. 准备测试数据 $testParams = ['user_id' => 456, 'content' => 'phpunit_test']; $closureRawReturn = 'raw_closure_output'; $expectedFinalResult = "formatted: {$closureRawReturn}"; // 2. 配置模拟Table对象 $mockTable = new class { public $action; public $params; }; $mockTable->params = $testParams; // 用引用变量捕获闭包的调用状态与传入参数 $isClosureCalled = false; $receivedParams = null; $mockTable->action = function($params) use (&$isClosureCalled, &$receivedParams, $closureRawReturn) { $isClosureCalled = true; $receivedParams = $params; return $closureRawReturn; }; // 3. 执行测试方法 $resolver = new Resolver(); $actualResult = $resolver->initiate($mockTable); // 4. 验证所有断言 $this->assertTrue($isClosureCalled, "Closure必须被执行当table的action是Closure类型时"); $this->assertEquals($testParams, $receivedParams, "Closure必须收到table的params参数"); $this->assertEquals($expectedFinalResult, $actualResult, "最终结果必须是resolveByType格式化后的内容"); } }
3. 进阶:验证resolveByType的调用细节
如果你想更严谨地确认resolveByType确实被调用且参数正确,可以用PHPUnit的部分Mock来单独验证这个方法的调用情况:
public function testInitiatePassesClosureResultToResolveByType() { $testParams = ['order_no' => 'OD-789']; $closureReturn = 'order_data'; // 配置模拟Table对象 $mockTable = new class { public $action; public $params; }; $mockTable->params = $testParams; $mockTable->action = fn() => $closureReturn; // 创建Resolver的部分Mock,仅mock resolveByType方法 $resolverMock = $this->getMockBuilder(Resolver::class) ->onlyMethods(['resolveByType']) ->getMock(); // 预期resolveByType会被调用1次,参数是闭包的返回值,同时指定模拟返回值 $mockFormattedResult = 'mock_formatted_order'; $resolverMock->expects($this->once()) ->method('resolveByType') ->with($closureReturn) ->willReturn($mockFormattedResult); // 执行并验证结果 $actualResult = $resolverMock->initiate($mockTable); $this->assertEquals($mockFormattedResult, $actualResult); }
额外提示
- 如果
$table是有严格封装的类(比如用getter/setter而非公共属性),需要调整Mock方式,比如用getMockBuilder模拟它的getAction()和getParams()方法。 - 别忘了补充非Closure场景的测试,确保逻辑分支覆盖完整(比如action是字符串、null等情况)。
内容的提问来源于stack exchange,提问作者jimy razit
相关产品推荐
相关产品推荐

