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

如何测试调用了受保护方法的类公共方法?三种测试方案选型咨询

结论

方案2是完全符合你需求的可行方案,不需要修改现有业务代码,也没有重复测试成本,用PHP主流测试框架PHPUnit的部分模拟(Partial Mock) 功能就能实现。


测试分层逻辑说明

你可以把测试拆成两个独立部分,完全解耦:

  • 对公共方法b、c的测试:仅验证对外接口逻辑是否正确,不需要关心内部方法d的实现
  • 对受保护方法d的单独测试:覆盖d的所有分支逻辑,只需要编写1次对应测试用例

具体实现代码(基于PHPUnit)

1. 公共方法b/c的测试

创建类a的部分模拟对象,仅替换受保护方法d,其他方法保留原有业务逻辑,不需要处理d内部依赖的service5、service6:

public function testB()
{
    // 仅mock d方法,其余方法保留原实现
    $mockA = $this->getMockBuilder(a::class)
        ->onlyMethods(['d'])
        ->getMock();

    // 注入mock后的service1、service2,配置对应返回值
    $mockService1 = $this->createMock(service1::class);
    $mockService1->method('getArg1')->willReturn('val1');
    $mockA->service1 = $mockService1;

    $mockService2 = $this->createMock(service2::class);
    $mockService2->method('getArg2')->willReturn('val2');
    $mockA->service2 = $mockService2;

    // 场景1:d返回真值,验证b返回true
    $mockA->method('d')
        ->with('val1', 'val2') // 可选校验传给d的参数是否正确
        ->willReturn('非空结果');
    $this->assertTrue($mockA->b());

    // 场景2:d返回null,验证b返回false
    $mockA->method('d')
        ->with('val1', 'val2')
        ->willReturn(null);
    $this->assertFalse($mockA->b());
}

方法c的测试逻辑和上面完全一致,仅需要替换注入的service3、service4即可。

2. 受保护方法d的单独测试

可以通过临时子类暴露受保护方法,仅在这一处处理service5、service6的mock,覆盖两个分支场景:

public function testD()
{
    // 创建临时子类,把d方法暴露为公共方法
    $testA = new class extends a {
        public function d($arg1, $arg2)
        {
            return parent::d($arg1, $arg2);
        }
    };

    // 注入mock后的service5、service6,配置对应返回值
    $mockService5 = $this->createMock(service5::class);
    // 按需配置service5的方法调用预期
    $testA->service5 = $mockService5;

    $mockService6 = $this->createMock(service6::class);
    // 按需配置service6的方法调用预期
    $testA->service6 = $mockService6;

    // 场景1:arg1等于arg2,验证返回结果符合预期
    $this->assertEquals('预期结果值', $testA->d('相同值', '相同值'));

    // 场景2:arg1不等于arg2,验证返回null
    $this->assertNull($testA->d('val1', 'val2'));
}

三种方案对比参考

  • 方案1:重复工作量大,后续d逻辑调整时需要同步修改b、c的所有测试用例,维护成本最高,不推荐
  • 方案3:符合单一职责设计原则,如果后续d的逻辑会扩展、会被其他类复用的话更推荐,但你不需要调整现有代码结构的话没必要选择
  • 方案2:零业务代码侵入,测试逻辑清晰,维护成本低,完全匹配你当前的需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 21:15:02