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

PHPUnit9 被Mock方法仍执行原逻辑的解决方法

PHPUnit Mock类方法时原方法仍执行问题排查

问题复现

单元测试中Mock Twilio服务API调用的初始代码:

$message = $twilioTest->getMockedMessageInstance(['body' => 'This won\'t exist']);
$twilioStub = $this->getMockBuilder(Twilio::class)->getMock();
$twilioStub->expects($this->once())->method('retrieveLastTextFromBot')->willReturn($message);

被Mock的retrieveLastTextFromBot方法原生实现逻辑:

$messages = $this->twilioClient->messages->read([
    'to' => TwilioDefinitions::getToNumber(),
    'from' => getenv('TWILIO_NUMBER'),
], 1);
if (count($messages) !== 1) {
     throw new NoExtantTextException('No previous message from ' . getenv('TWILIO_NUMBER') . ' to ' . TwilioDefinitions::getToNumber());
}
return $messages[0];

预期Mock后方法内部逻辑不会执行,直接返回预设的$message,但实际运行测试触发403报错:

1) CronControllerTest::testRemindMethodErrorHandling
Twilio\Exceptions\RestException: [HTTP 403] Unable to fetch page: Resource not accessible with Test Account Credentials

/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/vendor/twilio/sdk/src/Twilio/Page.php:58
/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/vendor/twilio/sdk/src/Twilio/Page.php:34
/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/vendor/twilio/sdk/src/Twilio/Rest/Api/V2010/Account/MessagePage.php:23
/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/vendor/twilio/sdk/src/Twilio/Rest/Api/V2010/Account/MessageList.php:147
/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/vendor/twilio/sdk/src/Twilio/Rest/Api/V2010/Account/MessageList.php:96
/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/vendor/twilio/sdk/src/Twilio/Rest/Api/V2010/Account/MessageList.php:118
/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/app/YmcaScheduler/Utility/Twilio.php:33
/Users/adammcgurk/Desktop/ymca-scheduler-brains/src/app/YmcaScheduler/Controller/CronController.php:29
/Users/adammcgurk/Desktop/ymca-scheduler-brains/tests/CronControllerTest.php:22

调用栈定位到Twilio.php第33行,即retrieveLastTextFromBot内部调用Twilio API的代码,证明原方法逻辑确实被执行。

解决方案

按出现概率从高到低排查修复:

  • 未显式指定要Mock的方法列表:旧版本PHPUnit调用getMock()无参时不会自动替换所有类方法,必须通过setMethods()(PHPUnit 8及以下版本)或onlyMethods()(PHPUnit 9及以上版本)指定要重写的方法,修改Mock生成代码:
// PHPUnit 9+ 写法
$twilioStub = $this->getMockBuilder(Twilio::class)
    ->onlyMethods(['retrieveLastTextFromBot'])
    ->getMock();
$twilioStub->expects($this->once())
    ->method('retrieveLastTextFromBot')
    ->willReturn($message);

注意:onlyMethods()仅接受类中真实存在的方法名,如果要Mock不存在的方法用addMethods()。

  • Mock实例未注入被测对象:检查CronController的初始化逻辑,确认生成的$twilioStub被正确注入到被测控制器实例中,排除被测类内部自行实例化Twilio对象、容器获取到真实Twilio实例的情况。
  • 被Mock方法不可重写:如果retrieveLastTextFromBot带有final、private、static修饰符,PHPUnit无法重写该方法,会直接执行原逻辑。此时需要调整代码结构,移除对应修饰符将方法改为可重写的public/protected非final方法,或直接Mock底层依赖的twilioClient实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 13:09:26