PHPUnit + Laravel单元测试常用技能
1.数据供给器
用来提供参数和结果,使用@dataProvider标注来指定使用哪个数据供给器方法。例如检测app升级数据是否符合预期,addProviderAppUpdateData()提供测试的参数和结果。testAppUpdateData()检测appUpdateData()返回的结果是否和给定的预期结果相等,即如果$appId='apple_3.3.2_117',$result=['status'=>0,'isIOS'=>false],则$data中如果含有['status'=>0,'isIOS'=>false],则断言成功。建议在数据提供器,逐个用字符串键名对其命名,这样在断言失败的时候将输出失败的名称,更容易定位问题。
示例代码:
appUpdateData(); $this->assertTrue(count(array_intersect_assoc($data,$result))==count($result)); } publicfunctionaddProviderAppUpdateData() { return[ 'null'=>[null,['status'=>0,'isIOS'=>false,'latest_version'=>'V']], 'errorappid'=>['sdas123123',['status'=>0,'isIOS'=>false,'latest_version'=>'V']], 'androidforceupdate'=>['bx7_3.3.5_120',['status'=>0,'isIOS'=>false]], 'iosforceupdate'=>['apple_3.3.2_117',['status'=>1,'isIOS'=>true]], 'androidsoftupdate'=>['sanxing_3.3.2_117',['status'=>2,'isIOS'=>false]], 'iossoftupdate'=>['apple_3.3.3_118',['status'=>2,'isIOS'=>true]], 'androidnormal'=>['fhqd_3.3.6_121',['status'=>1,'isIOS'=>false]], 'iosnormal'=>['apple_3.3.5_120',['status'=>1,'isIOS'=>true]], 'h5'=>['h5_3.3.3',['status'=>1,'isIOS'=>false]] ]; } }
断言成功结果:
2.断言方法
常用有assertTrue(),assertFalse(),assertNull(),assertEquals(),assertThat()。
assertThat()自定义断言。常用的约束有isNull()、isTrue()、isFalse()、isInstanceOf();常用的组合约束logicalOr()、logicalAnd()。例如检测返回的结果是否是null或ApiApp类。
示例代码:
assertThat($result,$this->logicalOr($this->isNull(),$this->isInstanceOf(ApiApp::class))); } publicfunctionadditionProviderGetLatestUpdateAppApi() { return[ 'apple'=>[1], 'android'=>[2], 'null'=>[9999] ]; } }
断言成功结果:
3.对异常进行测试
使用expectExceptionCode()对错误码进行检测,不建议对错误信息文案进行检测。例如检测设备被锁后是否抛出3026错误码。
示例代码:
expectExceptionCode(3026); Cache::put('device-login-error-account-','1,2,3,4,5',300); UserSecurityService::$request=null; UserSecurityService::$udid=null; UserSecurityService::deviceCheck(self::$userId); } }
断言成功结果:
4.测试私有属性和私有方法使用反射机制
如果只测试私有方法可使用ReflectionMethod()反射方法,使用setAccessible(true)设置方法可访问,并使用invokeArgs()或invoke()调用方法(invokeArgs将参数作为数组传递)。例如检测IP是否在白名单中。
示例代码:
被检测代码:
namespaceApp\Facades\Services; /** *ClassWebDefender */ classWebDefenderServiceextendsBaseService { //ip白名单 private$ipWhiteList=[ '10.*', '172.18.*', '127.0.0.1' ]; /** *ip是否在白名单中 * *@paramstring$ip * *@returnbool */ privatefunctioncheckIPWhiteList($ip) { if(!$this->ipWhiteList||!is_array($this->ipWhiteList)){ returnfalse; } foreach($this->ipWhiteListas$item){ if(preg_match("/{$item}/",$ip)){ returntrue; } } returnfalse; } }
检测方法:
setAccessible(true); $this->assertEquals($result,$checkIPWhiteList->invokeArgs(newWebDefenderService(),[$ip])); } publicfunctionadditionProviderIp() { return[ '10ip'=>['10.1.1.7',true], '172ip'=>['172.18.2.5',true], '127ip'=>['127.0.0.1',true], '192ip'=>['192.168.0.1',false] ]; } }
测试私有属性可使用ReflectionClass(),获取属性用getProperty(),设置属性的值用setValue(),获取方法用getMethod(),设置属性和方法可被访问使用setAccessible(true)。例如检测白名单路径。
示例代码:
被检测代码:
getPathInfo(),'/'); if(!$path||!$this->pathWhiteList||!is_array($this->pathWhiteList)){ returnfalse; } foreach($this->pathWhiteListas$item){ if(preg_match("/$item/",$path)){ returntrue; } } returnfalse; } }
检测方法:
getProperty('pathWhiteList'); $reflectedPathWhiteList->setAccessible(true); $reflectedPathWhiteList->setValue($webDefenderService,$pathProperty); $reflectedRequest=$reflectedClass->getProperty('request'); $reflectedRequest->setAccessible(true); $reflectedRequest->setValue($request); $reflectedMethod=$reflectedClass->getMethod('checkPathWhiteList'); $reflectedMethod->setAccessible(true); $this->assertEquals($result,$reflectedMethod->invoke($webDefenderService)); } publicfunctionadditionProviderPathWhiteList() { $allPath=['.*']; $checkPath=['^auth\/(.*)']; $authSendSmsRequest=newRequest([],[],[],[],[],['HTTP_HOST'=>'api.dev.com','REQUEST_URI'=>'/auth/sendSms']); $indexRequest=newRequest([],[],[],[],[],['HTTP_HOST'=>'api.dev.com','REQUEST_URI'=>'/']); $noMatchRequest=newRequest([],[],[],[],[],['HTTP_HOST'=>'api.dev.com','REQUEST_URI'=>'/product/sendSms']); return[ 'index'=>[[],$authSendSmsRequest,false], 'norequest'=>[$allPath,$indexRequest,false], 'allrequest'=>[$allPath,$authSendSmsRequest,true], 'checkauthsms'=>[$checkPath,$authSendSmsRequest,true], 'checkpathnomatch'=>[$checkPath,$noMatchRequest,false] ]; } }
5.代码覆盖率
使用--coverage-html导出的报告含有类与特质覆盖率、行覆盖率、函数与方法覆盖率。可查看当前单元测试覆盖的范围。例如输出WebDefenderTest的代码覆盖率到桌面(phpunittests/unit/WebDefenderTest--coverage-html~/Desktop/test)
6.指定代码覆盖率报告要包含哪些文件
在配置文件(phpunit.xml)里设置whitelist中的processUncoveredFilesFromWhitelist=true,设置目录用
示例代码:
./tests/Unit ./tests/Feature ./app/Services ./app/Facades/Services/WebDefenderService.php
7.参考文档
PHPUnit官方文档https://phpunit.readthedocs.io/zh_CN/latest/index.html
反射类https://www.php.net/manual/en/class.reflectionclass.php
反射方法https://www.php.net/manual/en/class.reflectionmethod.php
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。