PHPUnit测试报错:Mockery部分Mock调用executeActiveBlueprintRules方法不存在
解决Mockery Partial Mock调用真实方法时报错的问题
错误信息
Mockery\Exception\BadMethodCallException: Method Mockery_0__Tests_Unit_App_Utils_Blueprints_BlueprintRuleUtil::executeActiveBlueprintRules() does not exist on this mock object
问题原因
- 命名空间解析错误:测试类位于
Tests\Unit命名空间下,创建mock时使用App\Utils\Blueprints\BlueprintRuleUtil::class会被解析为相对命名空间(Tests\Unit\App\Utils\Blueprints\BlueprintRuleUtil),而非根命名空间下的目标类。Mockery基于错误的类名生成mock,该类实际不存在,导致mock类未继承正确的被测类,因此没有executeActiveBlueprintRules方法。 - 静态调用绕开mock:被测类中存在静态方法调用(
self::xxx、BlueprintRuleUtil::xxx),这类调用不会走mock实例,导致测试中mock的方法无法生效。
解决方案
1. 修正Mock创建的类名引用
测试类已通过use App\Utils\Blueprints\BlueprintRuleUtil;引入目标类,直接使用类名创建mock即可:
// 替换原mock创建代码 $blueprint_rule_util_partial_mock = \Mockery::mock(BlueprintRuleUtil::class)->makePartial();
或者使用完全限定名(前缀加反斜杠,确保解析为根命名空间类):
$blueprint_rule_util_partial_mock = \Mockery::mock(\App\Utils\Blueprints\BlueprintRuleUtil::class)->makePartial();
2. 将被测类中的静态调用改为实例调用
修改BlueprintRuleUtil类中的两处静态调用,确保方法调用走实例,让mock能拦截:
// 修改executeActiveBlueprintRules方法中的静态调用 // 原代码: // BlueprintRuleUtil::addDefaultOutsourceCostToStrategy($strategy, $active_blueprint_rule); // 修改为: $this->addDefaultOutsourceCostToStrategy($strategy, $active_blueprint_rule); // 修改addDefaultOutsourceCostToStrategy方法中的静态调用 // 原代码: // if (self::shouldAddDefaultOutsourceCost($strategy)) { // 修改为: if ($this->shouldAddDefaultOutsourceCost($strategy)) {
修正后的测试方法代码片段
public function testExecuteActiveBlueprintRulesShouldExecuteDefaultOutsourceCostRule() { Strategy::fake(); Strategy::create(['id' => 1]); $createdStrategy = Strategy::getCreatedModel(); // 使用正确的类名创建partial mock $blueprint_rule_util_partial_mock = \Mockery::mock(BlueprintRuleUtil::class)->makePartial(); $active_blueprint_rule = new stdClass(); $active_blueprint_rule->id = 1001; DB::shouldReceive("table") ->with('blueprint_rules') ->once()->andReturnSelf(); DB::shouldReceive("where") ->with('is_active', true) ->andReturnSelf(); DB::shouldReceive("get") ->andReturn([$active_blueprint_rule]); $blueprint_rule_util_partial_mock->shouldReceive('addDefaultOutsourceCostToStrategy') ->withAnyArgs(); $blueprint_rule_util_partial_mock->shouldReceive('shouldAddDefaultOutsourceCost')->withAnyArgs()->andReturn(true); $blueprint_rule_util_partial_mock->executeActiveBlueprintRules($createdStrategy); Strategy::stopFaking(); }
修正后的被测类核心代码片段
class BlueprintRuleUtil { const DEFAULT_OUTSOURCE_COST_RULE_ID = 1001; public function executeActiveBlueprintRules(Strategy $strategy) { if (!is_null($strategy)) { $active_blueprint_rules = DB::table('blueprint_rules') ->where('is_active', true) ->get(); foreach ($active_blueprint_rules as $active_blueprint_rule) { if (!is_null($active_blueprint_rule) && $active_blueprint_rule->id === self::DEFAULT_OUTSOURCE_COST_RULE_ID) { // 改为实例调用 $this->addDefaultOutsourceCostToStrategy($strategy, $active_blueprint_rule); } } } } public function addDefaultOutsourceCostToStrategy(Strategy $strategy, object $active_blueprint_rule) { // 改为实例调用 if ($this->shouldAddDefaultOutsourceCost($strategy)) { $department_slug = 'data-intelligence'; $department = DB::table('departments') ->where('slug', $department_slug) ->first(); if (!is_null($department)) { $strategy_cost = new StrategyCost; $strategy_cost->name = 'Snowflake'; $strategy_cost->department_id = $department->id; $strategy_cost->strategy_id = $strategy->id; $strategy_cost->month_1 = $active_blueprint_rule->amount; $strategy_cost->save(); $strategy_cost->strategy->recalculate(); } else { Log::debug("BlueprintRuleUtil: department $department_slug not found, did not add default outsource cost"); } } } }
内容的提问来源于stack exchange,提问作者Drew Gallagher
相关产品推荐
相关产品推荐

