使用PHPunit

背景

PHPUnit 是一個(gè)面向PHP開發(fā)者的測(cè)試框架胸完,可以寫提供編程代碼質(zhì)量,確保項(xiàng)目可以持續(xù)維護(hù)

安裝phpunit

項(xiàng)目不采用全局安裝 翘贮,我們使用composer安裝phpunit

composer require --dev phpunit/phpunit 5.7.27

備注:支持PHP5.6版本

創(chuàng)建配置文件

phpunit.xml放置在項(xiàng)目的根目錄中赊窥,是phpunit默認(rèn)讀取的配置文件,實(shí)現(xiàn)配置執(zhí)行單元測(cè)試執(zhí)行的初始化文件狸页,測(cè)試套件目錄
默認(rèn)情況下锨能,你應(yīng)用程序的 tests 目錄下包含兩個(gè)子目錄:Feature 和 Unit。

單元測(cè)試(Unit)是針對(duì)你的代碼中非常少芍耘,而且相對(duì)獨(dú)立的一部分代碼來進(jìn)行的測(cè)試址遇。實(shí)際上,大部分單元測(cè)試都是針對(duì)單個(gè)方法進(jìn)行的斋竞。

功能測(cè)試(Feature)能測(cè)試你的大部分代碼倔约,包括多個(gè)對(duì)象如何相互交互,甚至是對(duì) JSON 端點(diǎn)的完整 HTTP 請(qǐng)求窃页。 通常跺株,你的大多數(shù)測(cè)試應(yīng)該是功能測(cè)試。這些類型的測(cè)試可以最大程度地確保你的系統(tǒng)作為一個(gè)整體按預(yù)期運(yùn)行脖卖。

配置自動(dòng)加載

我們?cè)赾omposer.json添加autoload規(guī)則乒省,使用psr-4的自動(dòng)加載類

"require-dev": {
    "phpunit/phpunit": "5.7.27"
 },
"autoload-dev": {
    "psr-4": {
        "Tests\\": "tests/"
    }
}

執(zhí)行類自動(dòng)加載
composer dump-autoload --optimize

執(zhí)行單元測(cè)試

./vendor/bin/phpunit -c ./phpunit.xml

只允許以下指定用例

./vendor/bin/phpunit --filter OrderVehicleNewTest --debug

官方手冊(cè)

應(yīng)用到項(xiàng)目中實(shí)踐

配置phpunit.xml

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="./protect/__init__.php"
         colors="true"
         stopOnFailure="true"
         verbose="true"
>
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./protect</directory>
        </whitelist>
    </filter>
    <php> <!--https://www.kancloud.cn/manual/phpunit-book/68752-->
        <server name="APP_ENV" value="testing"/>
    </php>
</phpunit>

執(zhí)行腳本

<?php
require_once dirname(__FILE__) . '/../__init__.php';
class Script_Phpunit extends \PHPUnit_TextUI_Command{
    public static function main($exit = true){
        $command = new static;
        array_pop($_SERVER['argv']);
        return $command->run($_SERVER['argv'], $exit);
    }
}
//控制環(huán)境執(zhí)行
if(in_array(HLL_ENV,['dev','stg','pre_1'])){
    (new Script_Phpunit)->main();
}

應(yīng)用基礎(chǔ)類

<?php
namespace Tests;
use PHPUnit\Framework\TestCase;

abstract class TestBaseCase extends TestCase
{

    protected $commit = false; //是否提交事務(wù)



    /**
     * @notes: 測(cè)試用例類的第一個(gè)測(cè)試之前
     * @author: jackin.chen
     * @time: 2022/10/20 16:57
     * @function: setUpBeforeClass
     */
    public static function setUpBeforeClass()
    {

    }



    /**
     * @notes: 在運(yùn)行每個(gè)測(cè)試方法前自動(dòng)調(diào)用
     * @author: jackin.chen
     * @time: 2022/10/10 10:33
     * @function: setUp
     */
    protected function setUp()
    {
        //見:https://github.com/sebastianbergmann/phpunit/issues/1598
        if (!empty(\PHPUnit_Util_Blacklist::$blacklistedClassNames)) {
            foreach (\PHPUnit_Util_Blacklist::$blacklistedClassNames as $className => $parent) {
                try {
                    if (!class_exists($className)) {
                        unset(\PHPUnit_Util_Blacklist::$blacklistedClassNames[$className]);
                    }
                } catch (\Exception $e) {
                    unset(\PHPUnit_Util_Blacklist::$blacklistedClassNames[$className]);
                }
            }
        }
        //在配置xml直接配置啟動(dòng)文件
        //require_once (__DIR__ .'/../protect/__init__.php');
    }

    /**
     * @notes: 會(huì)在每個(gè)測(cè)試方法允許后被調(diào)用
     * @author: jackin.chen
     * @time: 2022/10/16 14:49
     * @function: tearDown
     */
    public function tearDown()
    {

    }


    /**
     * @notes: 對(duì)比JSON驗(yàn)證數(shù)據(jù)
     * @param $response
     * @param $expected
     * @param string $msg
     * @return $this
     * @author: jackin.chen
     * @time: 2022/10/20 13:49
     * @function: assertJsonString
     */
    protected function assertJsonString($response,$expected,$msg = 'msg'){

        $this->assertNotEmpty($response);
        $this->assertNotEmpty($expected);
        $this->assertArrayHasKey($msg,$response);
        $expect = $decoded = [];
        foreach ($expected as $key => $value){
            if(isset($response[$key])){
                $expect[$key] = $value; // 期望值
                $decoded[$key] = $response[$key]; //返回值
            }
        }
        $message =  isset($response[$msg]) ? $response[$msg] : '';
        $expectedJson = json_encode($expect, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        $actualJson = json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        $this->assertJsonStringEqualsJsonString($expectedJson,$actualJson,$message);
        return $this;
    }


    /**
     * @notes:
     * @param string $uri 路由地址
     * @param mixed $parameters 參數(shù)
     * @return false|mixed
     * @author: jackin.chen
     * @time: 2022/10/10 10:21
     * @function: call
     */
    protected function call($uri,$parameters){
        $uri = self::prepareUrlForRequest($uri);
        list($class,$action) = explode('/',$uri);
        $_GET['_m'] = $class;
        $_GET['_a'] = $action;
        $controller = 'Ctrller_'.ucfirst($class);
        ob_start();
        $instance = new $controller;
        $result = call_user_func_array([$instance, $action], [$parameters]);
        if(!is_array($result)){
            $json = ob_get_contents();
            $result =  \Lib_Func::jsonDecode($json);
        }
        ob_end_clean();
        return  $result;
    }


    /**
     * @notes: 下劃線轉(zhuǎn)大駝峰
     * @param string $words
     * @param string $separator
     * @return string
     * @author: jackin.chen
     * @time: 2022/10/10 10:16
     * @function: prepareUrlForRequest
     */
    protected function prepareUrlForRequest($words,$separator='_')
    {
        $words = $separator. str_replace($separator, " ", strtolower($words));
        return ltrim(str_replace(" ", "", ucwords($words)), $separator );
    }

    /**
     * @notes: 使用事務(wù)調(diào)試接口避免臟數(shù)據(jù)
     * @param \Closure $callback
     * @param array $parameters
     * @return false|mixed
     * @author: jackin.chen
     * @time: 2022/10/10 11:18
     * @function: transaction
     */
    protected function transaction(\Closure $callback,$parameters=[]){
        $model_common = new \Model_Common();
        $model_common->TransStart();
        $result = call_user_func($callback,$parameters); //執(zhí)行回調(diào)函數(shù)
        if($this->commit){
            $model_common->Commit();
        }else{
            $model_common->RollBack();
        }
        return $result;
    }
}

應(yīng)用業(yè)務(wù)類

<?php

namespace Tests\Feature;

use Tests\TestBaseCase;
use Tests\Data\OrderVehicleJson;

class OrderVehicleNewTest extends TestBaseCase
{
    protected $commit = false;

    protected $ret = 0;


    /**
     * @notes: 業(yè)務(wù)線數(shù)據(jù)
     * @author: jackin.chen
     * @time: 2022/11/8 16:35
     * @function: testBusinessList
     */
    public function testBusinessList()
    {
        $response = $this->call('order_vehicle_new/business',[]);
        $this->assertJsonString($response,['ret' => $this->ret]);
        $this->assertNotEmpty($response['data']);
    }


    /**
     * @notes: 查詢數(shù)據(jù)供給器
     * @return \int[][]
     * @author: jackin.chen
     * @time: 2022/10/20 14:17
     * @function: additionProvider
     */
    public function vehicleStandardProvider()
    {
        //類型、國(guó)標(biāo)ID畦木、期望值
        return array(
            array(\Input_OrderVehicleNew::IS_STATUS1, \Input_OrderVehicleNew::VEHICLE_ATTR0,$this->ret),
            array(\Input_OrderVehicleNew::IS_STATUS1, \Input_OrderVehicleNew::VEHICLE_ATTR1,$this->ret),
            array(\Input_OrderVehicleNew::IS_STATUS2, \Input_OrderVehicleNew::VEHICLE_ATTR0,$this->ret),
            array(\Input_OrderVehicleNew::IS_STATUS2, \Input_OrderVehicleNew::VEHICLE_ATTR1,$this->ret)
        );
    }


    /**
     * @notes: 所屬國(guó)標(biāo)訂單車型
     * @param $vehicle_attr_type
     * @param $status
     * @param $expected
     * @author: jackin.chen
     * @time: 2022/11/8 16:41
     * @function: testVehicleStandardList
     * @dataProvider vehicleStandardProvider
     */
    public function testVehicleStandardList($vehicle_attr_type,$status,$expected)
    {
        $params =  [
            'vehicle_attr_type'=>$vehicle_attr_type,
            'status'=>$status
        ];
        $response = $this->call('order_vehicle_standard/a_get_list_all',$params);
        $this->assertJsonString($response,['ret' => $expected]);
        $this->assertNotEmpty($response['data']);
    }


    /**
     * @notes: 獲取關(guān)聯(lián)國(guó)標(biāo)車型的圖片信息二級(jí)下拉框信息
     * @author: jackin.chen
     * @time: 2022/11/9 11:05
     * @function: testGetImgOption
     */
    public function testGetImgOption()
    {
        $params =  [];
        $response = $this->call('order_vehicle_standard/a_get_img_option',$params);
        $this->assertJsonString($response,['ret' => $this->ret]);
        $this->assertNotEmpty($response['data']);
    }


    /**
     * @notes:
     * @param $params
     * @author: jackin.chen
     * @time: 2022/11/9 13:53
     * @function: testOrderVehicleNewAdd
     */
    public function testOrderVehicleNewAdd()
    {
        $json = file_get_contents(__DIR__.'/../Data/add_order_vehicle.json');
        $params = json_decode($json,true);
        $response = $this->call('order_vehicle_new/add_order_vehicle',$params);
        $this->assertJsonString($response,['ret' => $this->ret]);
        $this->assertNotEmpty($response['data']);
    }


}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袖扛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛆封,老刑警劉巖唇礁,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惨篱,居然都是意外死亡盏筐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門砸讳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琢融,“玉大人,你說我怎么就攤上這事簿寂⊙В” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵常遂,是天一觀的道長(zhǎng)纳令。 經(jīng)常有香客問我,道長(zhǎng)克胳,這世上最難降的妖魔是什么平绩? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮毯欣,結(jié)果婚禮上馒过,老公的妹妹穿的比我還像新娘。我一直安慰自己酗钞,他們只是感情好腹忽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著砚作,像睡著了一般窘奏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葫录,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天着裹,我揣著相機(jī)與錄音,去河邊找鬼米同。 笑死骇扇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的面粮。 我是一名探鬼主播少孝,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼熬苍!你這毒婦竟也來了稍走?” 一聲冷哼從身側(cè)響起袁翁,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎婿脸,沒想到半個(gè)月后粱胜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狐树,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年焙压,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抑钟。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冗恨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出味赃,到底是詐尸還是另有隱情,我是刑警寧澤虐拓,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布心俗,位于F島的核電站,受9級(jí)特大地震影響蓉驹,放射性物質(zhì)發(fā)生泄漏城榛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一态兴、第九天 我趴在偏房一處隱蔽的房頂上張望狠持。 院中可真熱鬧,春花似錦瞻润、人聲如沸喘垂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)正勒。三九已至,卻和暖如春傻铣,著一層夾襖步出監(jiān)牢的瞬間章贞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工非洲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸭限,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓两踏,卻偏偏與公主長(zhǎng)得像败京,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缆瓣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容