進階篇:如何爲ThinkPHP5編寫優質的單元測試?

前言

在項目開發中,須要使用到ThinkPHP 5,爲了編寫單元測試,解決了幾個難題,特此紀錄分享一下。php

 

難點1:TP5自帶的單元測試感受很差用,如何使用純粹的原生PHPUnit?

在看雲上,有TP5官方關於單元測試的使用說明,連接是:https://www.kancloud.cn/manual/thinkphp5/182511html

但上面的說明過於簡單,對於實際使用幫助有限。git

 

對於一直鍾情於自動化單元測試以及PHPUnit原生單元測試的我,決定對此優化一番,引入並在ThinkPHP 5下使用原生PHPUnit。github

第一步:準備工做

在tests目錄下,建立一個phpunit目錄,而後建立兩個文件:測試啓動文件bootstrap.php和單元測試的配置文件phpunit.xml。thinkphp

測試啓動文件bootstrap.php,能夠參考項目的啓動文件,複製過來後調整下,例如這樣:bootstrap

<?php
// 定義應用目錄
define('APP_PATH', __DIR__ . '/../../application/');

define('APP_DEBUG', true);     //開啓調試模式
define("APP_STATUS", "tests"); //定義爲本地環境
define("RUNTIME_PATH", __DIR__ . "/../../runtime/"); //定義緩存目錄

require APP_PATH . '/define.php';

// ThinkPHP 引導文件
// 加載基礎文件
require __DIR__ . '/../../thinkphp/base.php';

// 加載應用
\think\Loader::addNamespace('app', APP_PATH);

// 兼容舊版本的PHPUnit
if (!class_exists('PHPUnit_Framework_TestCase')) {
    class PHPUnit_Framework_TestCase extends PHPUnit\FrameWork\TestCase {
    }
}

// 手動再引入一次測試配置
\think\Config::load(APP_PATH . '/tests/test.php');

// 手動引入框架和應用的函數
require_once APP_PATH . '../thinkphp/helper.php';
require_once APP_PATH . 'common.php';

對於phpunit.xml配置文件,能夠這樣寫:api

<?xml version="1.0" encoding="UTF-8"?>

<phpunit backupGlobals="false"
    backupStaticAttributes="false"
    colors="true"
    bootstrap="./bootstrap.php"
    convertErrorsToExceptions="true"
    convertNoticesToExceptions="true"
    convertWarningsToExceptions="true"
    processIsolation="false"
    stopOnFailure="false"
    syntaxCheck="false"
    >
    <php>
        <ini name="intl.default_locale" value="en"/>
        <ini name="intl.error_level" value="0"/>
        <ini name="memory_limit" value="-1"/>
    </php>

    <testsuites>
        <testsuite name="Test Suite">
            <directory suffix="Test.php">./</directory>
            <directory suffix="Test.php">./application</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">../../application</directory>
        </whitelist>
    </filter>
</phpunit>

第二步:寫個簡單的demo

下面編寫一個簡單的測試用例,試運行一下。在phpunit下建立一個demo目錄,並建立TestCaseClass.php文件。此時結構以下:緩存

$ tree ./phpunit 
./phpunit
├── bootstrap.php
├── demo
│   └── TestCaseClass.php
└── phpunit.xml

TestCaseClass.php文件代碼是:bash

<?php
use PHPUnit\Framework\TestCase;

namespace tests\demo;

class TestCaseClass extends \PHPUnit_Framework_TestCase
{
    public function testHere()
    {
        $this->assertTrue(true);
    }
}

這樣編寫成功後,就能夠試運行了。app

第三步:運行一個簡單的PHPUnit測試

$ phpunit ./demo/TestCaseClass.php    
PHPUnit 5.7.25 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 121 ms, Memory: 10.00MB

OK (1 test, 1 assertion)

後面就能夠繼續這樣編寫原生的PHPUnit單元測試啦。

 

難點2:如何使用PhalApi開源框架的腳本,爲TP5項目自動生成測試代碼?

下面就要爲TP5的項目代碼編寫單元測試了,但每次都要人工手動重複編寫測試代碼是一件很累人、很耗時、很低率的工做。有沒更好的技能?有!

參考我曾經編寫的腳本工具:[PHPUnit]自動生成PHPUnit測試骨架腳本-提供您的開發效率【2015升級版】

如今已經成爲PhalApi開源框架內置的腳本命令了。PhalApi是一個專一於接口開發的開源框架,所以咱們能夠把PhalApi的phalapi-buildtest腳本命令整合到ThinkPHP 5 的項目裏,

 

很是重要的連接

使用說明和詳細的官方文檔:PhalApi 2.x 單元測試

腳本命令Github下載地址(須要同時下載這兩個文件):

https://github.com/phalapi/phalapi/blob/master-2x/bin/phalapi-buildtest

https://github.com/phalapi/phalapi/blob/master-2x/bin/build_test.php

 

下載後放到tests/phpunit目錄下,此時文件結構以下:

$ tree ./phpunit 
./phpunit
├── bootstrap.php
├── build_test.php
├── demo
│   └── TestCaseClass.php
├── phalapi-buildtest
└── phpunit.xml

 

準備好後,就能夠開始生成單元測試的代碼啦!

例如,可執行:

$ ./phalapi-buildtest ../../application/controller/Site.php 'app\controller\Site' > ./controller/SiteTest.php

生成後,即可執行。

 

難點3:如何測試controller,以及如何解決input()的參數緩存?

正常狀況下,進行單個單元測試時,如下測試代碼是能夠的:

public function testLogin()
    {
        $_POST['email'] = 'phpunit123';
        $_POST['password'] = '123456';
        $_POST['remember'] = '1';

        $rs = $this->appcontrollerSite->login();

        $this->assertEquals(1, $rs['code']);
    }

對應的源代碼是:

<?php

class Site extends Controller {

    public function login() {
        $username = input('post.email');
        $password = input('post.password');
        $remember = input('post.remember/d', 0);

        // todo
    }
}

可是,若是執行所有單元測試的話,傳給controller的$_POST參數就失效了。這是由於ThinkPHP5的Request是一個單例,而且在think\Request::$post變量中緩存了POST參數,致使後面的參數不生效。

爲此,須要這樣調整傳遞參數:

public function testLogin()
    {
        $_POST['email'] = 'phpunit123';
        $_POST['password'] = '123456';
        $_POST['remember'] = '1';

        // 加多這兩行,重置POST參數
        $params = ['POST' => $_POST];
        \think\Request::create('/', 'POST', $params);

        $rs = $this->appcontrollerSite->login();

        $this->assertEquals(1, $rs['code']);
    }

參考

發現了一篇寫得很讚的文章:PHPUnit簡介及使用(thinkphp5的單元測試安裝及使用)

相關文章
相關標籤/搜索