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

如何使用Mockery处理私有类?Mock私有方法getRandNumber()遇错求解

解决PHPUnit Mock私有方法的报错问题

首先咱们来理清楚报错的原因:getRandNumber()是私有方法,而PHPUnit的createPartialMock和MockBuilder只能配置public或protected的方法。你把私有方法放进partial mock的方法列表里,PHPUnit根本识别不到这个方法可以被配置,所以才抛出了那个警告。

下面给你几个可行的解决方案,按推荐程度排序:

1. 重构代码,抽离随机数生成逻辑(最佳实践)

这是最推荐的做法,不仅能解决测试问题,还能让代码职责更清晰,符合依赖注入的设计原则。

把随机数生成的逻辑单独抽成一个类,让MyModel依赖这个类:

// 新建负责生成随机数的类
class RandomNumberGenerator {
    public function generate(): int {
        return rand(); // 原来的随机逻辑
    }
}

// 修改MyModel,通过构造函数注入依赖
class MyModel {
    private $randomGenerator;

    public function __construct(RandomNumberGenerator $randomGenerator) {
        $this->randomGenerator = $randomGenerator;
    }

    // 原来调用私有getRandNumber()的方法,改为调用依赖的方法
    public function methodToTest() {
        $randNum = $this->randomGenerator->generate();
        // 你的业务逻辑...
        return $randNum * 2;
    }
}

测试时直接Mock这个依赖类即可,完全不需要处理私有方法:

public function testMethodToTest() {
    // Mock随机数生成器,让它始终返回1
    $mockGenerator = $this->createMock(RandomNumberGenerator::class);
    $mockGenerator->method('generate')->willReturn(1);

    // 实例化MyModel并传入Mock依赖
    $myModel = new MyModel($mockGenerator);

    // 执行测试并断言结果
    $result = $myModel->methodToTest();
    $this->assertEquals(2, $result);
}

2. 将私有方法改为protected(允许修改原代码时适用)

如果不想大改代码,只是为了测试方便,可以把getRandNumber()的访问修饰符从private改成protected。这样PHPUnit的partial mock就能识别并配置它了:

修改MyModel:

class MyModel {
    protected function getRandNumber(): int {
        return rand();
    }

    public function methodToTest() {
        $randNum = $this->getRandNumber();
        // 业务逻辑...
    }
}

测试代码可以简化成这样,不用再写反射相关的代码:

public function testMethodToTest() {
    $myModel = $this->createPartialMock(MyModel::class, ['getRandNumber']);
    $myModel->method('getRandNumber')->willReturn(1);

    // 执行测试并断言预期结果
    $result = $myModel->methodToTest();
    $this->assertEquals(/* 你的预期结果 */, $result);
}

3. 不修改原代码,用匿名类临时覆盖(仅作为最后备选)

如果完全不能修改原代码,可以尝试用匿名类继承MyModel并重新定义方法,但要注意:这种方式仅当父类的测试方法调用的是可继承的方法时有效,如果原方法是严格私有,子类无法覆盖内部调用。示例如下:

public function testMethodToTest() {
    // 创建匿名类继承MyModel,重写getRandNumber为protected并返回1
    $myModel = new class extends MyModel {
        protected function getRandNumber(): int {
            return 1;
        }
    };

    // 执行测试并断言
    $result = $myModel->methodToTest();
    $this->assertEquals(/* 预期结果 */, $result);
}

这种方式比较hack,维护性差,只建议在完全无法修改原代码的情况下使用。

总结一下,优先选择重构抽离依赖的方式,其次是修改方法访问修饰符,最后才考虑临时hack方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 19:42:35