使用PHPUnit進(jìn)行單元測(cè)試并生成代碼覆蓋率報(bào)告的方法
安裝PHPUnit
使用 Composer 安裝 PHPUnit
#查看composer的全局bin目錄 將其加入系統(tǒng) path 路徑 方便后續(xù)直接運(yùn)行安裝的命令 composer global config bin-dir --absolute #全局安裝 phpunit composer global require --dev phpunit/phpunit #查看版本 phpunit --version
使用Composer構(gòu)建你的項(xiàng)目
我們將新建一個(gè)unit項(xiàng)目用于演示單元測(cè)試的基本工作流
創(chuàng)建項(xiàng)目結(jié)構(gòu)
mkdir unit && cd unit && mkdir app tests reports #結(jié)構(gòu)如下 ./ ├── app #存放業(yè)務(wù)代碼 ├── reports #存放覆蓋率報(bào)告 └── tests #存放單元測(cè)試
使用Composer構(gòu)建工程
#一路回車即可
composer init
#注冊(cè)命名空間
vi composer.json
...
"autoload": {
"psr-4": {
"App\\": "app/",
"Tests\\": "tests/"
}
}
...
#更新命名空間
composer dump-autoload
#安裝 phpunit 組件庫(kù)
composer require --dev phpunit/phpunit
到此我們就完成項(xiàng)目框架的構(gòu)建,下面開始寫業(yè)務(wù)和測(cè)試用例。
編寫測(cè)試用例
創(chuàng)建文件app/Example.php 這里我為節(jié)省排版就不寫注釋了
<?php
namespace App;
class Example
{
private $msg = "hello world";
public function getTrue()
{
return true;
}
public function getFalse()
{
return false;
}
public function setMsg($value)
{
$this->msg = $value;
}
public function getMsg()
{
return $this->msg;
}
}
創(chuàng)建相應(yīng)的測(cè)試文件tests/ExampleTest.php
<?php
namespace Tests;
use PHPUnit\Framework\TestCase as BaseTestCase;
use App\Example;
class ExampleTest extends BaseTestCase
{
public function testGetTrue()
{
$example = new Example();
$result = $example->getTrue();
$this->assertTrue($result);
}
public function testGetFalse()
{
$example = new Example();
$result = $example->getFalse();
$this->assertFalse($result);
}
public function testGetMsg()
{
$example = new Example();
$result = $example->getTrue();
// $result is world not big_cat
$this->assertEquals($result, "hello big_cat");
}
}
執(zhí)行單元測(cè)試
[root@localhost unit]# phpunit --bootstrap=vendor/autoload.php \ tests/ PHPUnit 6.5.14 by Sebastian Bergmann and contributors. ..F 3 / 3 (100%) Time: 61 ms, Memory: 4.00MB There was 1 failure: 1) Tests\ExampleTest::testGetMsg Failed asserting that 'hello big_cat' matches expected true. /opt/unit/tests/ExampleTest.php:27 /root/.config/composer/vendor/phpunit/phpunit/src/TextUI/Command.php:195 /root/.config/composer/vendor/phpunit/phpunit/src/TextUI/Command.php:148 FAILURES! Tests: 3, Assertions: 3, Failures: 1.
這是一個(gè)非常簡(jiǎn)單的測(cè)試用例類,可以看到,執(zhí)行了共3個(gè)測(cè)試用例,共3個(gè)斷言,共1個(gè)失敗,可以參照PHPUnit手冊(cè)學(xué)習(xí)更多高級(jí)用法。
代碼覆蓋率
代碼覆蓋率反應(yīng)的是測(cè)試用例對(duì)測(cè)試對(duì)象的行,函數(shù)/方法,類/特質(zhì)的訪問率是多少(PHP_CodeCoverage 尚不支持 Opcode覆蓋率、分支覆蓋率 及 路徑覆蓋率),雖然有很多人認(rèn)為過分看重覆蓋率是不對(duì)的,但我們初入測(cè)試還是俗氣的追求一下吧。
測(cè)試覆蓋率的檢測(cè)對(duì)象是我們的業(yè)務(wù)代碼,PHPUnit通過檢測(cè)我們編寫的測(cè)試用例調(diào)用了哪些函數(shù),哪些類,哪些方法,每一個(gè)控制流程是否都執(zhí)行了一遍來計(jì)算覆蓋率。
PHPUnit 的覆蓋率依賴 Xdebug,可以生成多種格式:
--coverage-clover <file> Generate code coverage report in Clover XML format. --coverage-crap4j <file> Generate code coverage report in Crap4J XML format. --coverage-html <dir> Generate code coverage report in HTML format. --coverage-php <file> Export PHP_CodeCoverage object to file. --coverage-text=<file> Generate code coverage report in text format. --coverage-xml <dir> Generate code coverage report in PHPUnit XML format.
同時(shí)需要使用 --whitelist dir參數(shù)來設(shè)定我們需要檢測(cè)覆蓋率的業(yè)務(wù)代碼路徑,下面演示一下具體操作:
phpunit \ --bootstrap vendor/autoload.php \ --coverage-html=reports/ \ --whitelist app/ \ tests/ #查看覆蓋率報(bào)告 cd reports/ && php -S 0.0.0.0:8899


這樣我們就對(duì)業(yè)務(wù)代碼App\Example做單元測(cè)試,并且獲得我們單元測(cè)試的代碼覆蓋率,現(xiàn)在自然是百分之百,因?yàn)槲业臏y(cè)試用例已經(jīng)訪問了App\Example的所有方法,沒有遺漏的,開發(fā)中則能體現(xiàn)出你的測(cè)試時(shí)用力對(duì)業(yè)務(wù)代碼測(cè)試度的完善性。
基境共享測(cè)試數(shù)據(jù)
可能你會(huì)發(fā)現(xiàn)我們?cè)诿總€(gè)測(cè)試方法中都創(chuàng)建了App\Example對(duì)象,在一些場(chǎng)景下是重復(fù)勞動(dòng),為什么不能只創(chuàng)建一次然后供其他測(cè)試方法訪問呢?這需要理解 PHPUnit 執(zhí)行測(cè)試用例的工作流程。
我們沒有辦法在不同的測(cè)試方法中通過某成員屬性來傳遞數(shù)據(jù),因?yàn)槊總€(gè)測(cè)試方法的執(zhí)行都是新建一個(gè)測(cè)試類對(duì)象,然后調(diào)用相應(yīng)的測(cè)試方法。
即測(cè)試的執(zhí)行模式并不是
testObj = new ExampleTest(); testObj->testMethod1(); testObj->testMethod2();
而是
testObj1 = new ExampleTest(); testObj1->testMethod1(); testObj2 = new ExampleTest(); testObj2->testMethod2();
所以testMethod1()修改的屬性狀態(tài)無法傳遞給 testMethod2()使用。
PHPUnit則為我們提供了全面的hook接口:
public static function setUpBeforeClass()/tearDownAfterClass()//測(cè)試類構(gòu)建/解構(gòu)時(shí)調(diào)用 protected function setUp()/tearDown()//測(cè)試方法執(zhí)行前/后調(diào)用 protected function assertPreConditions()/assertPostConditions()//斷言前/后調(diào)用
當(dāng)運(yùn)行測(cè)試時(shí),每個(gè)測(cè)試類大致就是如下的執(zhí)行步驟
#測(cè)試類基境構(gòu)建 setUpBeforeClass #new一個(gè)測(cè)試類對(duì)象 #第一個(gè)測(cè)試用例 setUp assertPreConditions assertPostConditions tearDown #new一個(gè)測(cè)試類對(duì)象 #第二個(gè)測(cè)試用例 setUp assertPreConditions assertPostConditions tearDown ... #測(cè)試類基境解構(gòu) tearDownAfterClass
所以我們可以在測(cè)試類構(gòu)建時(shí)使用setUpBeforeClass創(chuàng)建一個(gè) App\Example 對(duì)象作為測(cè)試類的靜態(tài)成員變量(tearDownAfterClass主要用于一些資源清理,比如關(guān)閉文件,數(shù)據(jù)庫(kù)連接),然后讓每一個(gè)測(cè)試方法用例使用它:
<?php
namespace Tests;
use App\Example;
use PHPUnit\Framework\TestCase as BaseTestCase;
class ExampleTest extends BaseTestCase
{
// 類靜態(tài)屬性
private static $example;
public static function setUpBeforeClass()
{
self::$example = new Example();
}
public function testGetTrue()
{
// 類的靜態(tài)屬性更新
self::$example->setMsg("hello big_cat");
$result = self::$example->getTrue();
$this->assertTrue($result);
}
public function testGetFalse()
{
$result = self::$example->getFalse();
$this->assertFalse($result);
}
/**
* 依賴 testGetTrue 執(zhí)行完畢
* @depends testGetTrue
* @return [type] [description]
*/
public function testGetMsg()
{
$result = self::$example->getMsg();
$this->assertEquals($result, "hello big_cat");
}
}
或者使用@depends注解來聲明二者的執(zhí)行順序,并使用傳遞參數(shù)的方式來滿足需求。
public function testMethod1()
{
$this->assertTrue(true);
return "hello";
}
/**
* @depends testMethod1
*/
public function testMethod2($str)
{
$this->assertEquals("hello", $str);
}
#執(zhí)行模式大概如下 testObj1 = new Test; $str = testObj1->testMethod1(); testObj2 = new Test; testObj2->testMethod2($str);
理解測(cè)試執(zhí)行的模式還是很有幫助的,其他高級(jí)特性請(qǐng)瀏覽官方文檔。
使用phpunit.xml編排測(cè)試套件
使用測(cè)試套件來管理測(cè)試,vi phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="./vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<!--可以定義多個(gè) suffix 用于指定待執(zhí)行的測(cè)試類文件后綴-->
<testsuite name="Tests">
<directory suffix="Test.php">./test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<!--可以定義多個(gè) 對(duì)./app下的業(yè)務(wù)代碼做覆蓋率統(tǒng)計(jì)-->
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<logging>
<!--覆蓋率報(bào)告生成類型和輸出目錄 lowUpperBound低覆蓋率閾值 highLowerBound高覆蓋率閾值-->
<log type="coverage-html" target="./reports" lowUpperBound="35" highLowerBound="70"/>
</logging>
</phpunit>
然后直接運(yùn)phpunit行即可:
[root@localhost unit]# phpunit PHPUnit 6.5.14 by Sebastian Bergmann and contributors. Time: 81 ms, Memory: 4.00MB No tests executed! Generating code coverage report in HTML format ... done
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
php注冊(cè)和登錄界面的實(shí)現(xiàn)案例(推薦)
下面小編就為大家?guī)硪黄猵hp注冊(cè)和登錄界面的實(shí)現(xiàn)案例(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10
PHP實(shí)現(xiàn)采集中國(guó)天氣網(wǎng)未來7天天氣
這篇文章主要介紹了PHP實(shí)現(xiàn)采集中國(guó)天氣網(wǎng)未來7天天氣方法,本文詳細(xì)的講解了需求的實(shí)現(xiàn),也可以做為學(xué)習(xí)PHP采集的入門教程,需要的朋友可以參考下2014-10-10
在WordPress的文章編輯器中設(shè)置默認(rèn)內(nèi)容的方法
這篇文章主要介紹了在WordPress的文章編輯器中設(shè)置默認(rèn)內(nèi)容的方法,包括給不同類型的文章設(shè)置不同內(nèi)容的具體方法,需要的朋友可以參考下2015-12-12
PHP獲取當(dāng)前時(shí)間的5種實(shí)現(xiàn)方式
這篇文章主要介紹了PHP獲取當(dāng)前時(shí)間的5種實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
PHP list() 將數(shù)組中的值賦給變量的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄狿HP list() 將數(shù)組中的值賦給變量的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06
laravel 判斷查詢數(shù)據(jù)庫(kù)返回值的例子
今天小編就為大家分享一篇laravel 判斷查詢數(shù)據(jù)庫(kù)返回值的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-10-10
php二維數(shù)組排序與默認(rèn)自然排序的方法介紹
本篇文章介紹了,在php中二維數(shù)組排序與默認(rèn)自然排序的方法。需要的朋友參考下2013-04-04
yii2使用ajax返回json的實(shí)現(xiàn)方法
這篇文章主要介紹了yii2使用ajax返回json的實(shí)現(xiàn)方法,實(shí)例分析了Yii框架使用ajax調(diào)用數(shù)據(jù)及返回json格式數(shù)據(jù)的相關(guān)技巧,需要的朋友可以參考下2016-05-05

