PHPUnit如何对未定义在类中的全局函数进行mock?
不封装类实现PHPUnit全局函数Mock的方案
createMock() 本身是面向类方法设计的工具,原生不支持直接生成全局函数的模拟,不想把全局函数封装到类的话,可以用以下两种方案实现:
利用PHP命名空间函数加载优先级覆写
PHP调用非全限定名的函数时,会优先查找当前代码所在命名空间下的同名函数,找不到才会去全局空间查找。你可以直接在测试文件的对应命名空间下,定义和目标函数同名的模拟实现,业务代码执行时就会自动调用你写的模拟版本,不需要修改原有业务代码。
举个例子,如果getOwner定义在App\Service命名空间下,测试代码可以这么写:namespace App\Service; // 测试环境下优先被调用的模拟函数 function getOwner($wsn) { return $wsn === 'valid_test_wsn' ? 'mock_user' : ''; } class OwnerServiceTest extends \PHPUnit\Framework\TestCase { public function testOwnerLogic() { // 业务内部调用getOwner时会自动走上面的模拟实现 $result = doSomethingThatCallGetOwner('valid_test_wsn'); $this->assertEquals('mock_user', $result); } }注意这个方案生效的前提是,业务代码调用
getOwner时没有写全全局全限定名(也就是没有写成\getOwner()),如果原代码写死了全限定名调用,这个方案无法生效。借助支持全局函数模拟的PHPUnit扩展
有专门适配PHPUnit的全局函数模拟扩展,不需要你手动写命名空间覆写代码,直接通过API就可以声明要模拟的全局函数、定义入参匹配规则和返回值,哪怕原函数是定义在根命名空间、或者原代码用全限定名调用也可以正常模拟,不需要重构业务代码封装类。
基础用法示例:use PHPUnit\Framework\TestCase; class GlobalFuncTest extends TestCase { // 引入扩展提供的Mock能力trait use \phpmock\phpunit\PHPMock; public function testMockGlobalGetOwner() { // 声明要模拟指定命名空间下的getOwner函数 $mock = $this->getFunctionMock('App\Service', 'getOwner'); // 定义预期入参和返回值 $mock->expects($this->once()) ->with('wsn_001') ->willReturn('mocked_username'); // 执行业务逻辑验证即可 $this->assertEquals('mocked_username', callTargetFunc('wsn_001')); } }
如果你的全局函数直接定义在根命名空间,优先选第二种扩展方案,避免手动覆写出现函数重复定义的报错。
内容的提问来源于stack exchange,提问作者mrswagbird
相关产品推荐
相关产品推荐

