如何使用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
相关产品推荐
相关产品推荐

