PHP單元測試框架PHPUnit的使用

attachments-2020-06-jDzVTc1C5edddfc9e635e.png

單元測試是開發過程當中必不可少的一環,一個項目有良好的單元測試代碼,重構的勇氣都大不少。此次寫一篇小文來介紹一下 PHP 的單元測試工具 PHPUnit 的使用。php

PHPUnit 的使用並不難,這篇文章主要仍是充當一個引子,介紹基本概念和使用,有了這篇文章的基礎以後,去看官網的文檔就會更加順風順水。

安裝

安裝 PHPUnit 的方式很簡單,使用 composer 能夠一行代碼就能夠安裝。html

composer require --dev phpunit/phpunit

安裝以後,在 vendor/bin 目錄下有一個 phpunit 的可執行文件,這個就是 phpunit 本體了。假設咱們項目的目錄結構以下:json

➜  phpunit tree .

├── controller
├── model
├── service
├── test
└── vendor
├── composer.json

其中咱們的單元測試代碼都放在 test 目錄下。使用 composer 來爲咱們解決 autoload 的問題。segmentfault

{
  "autoload": {
    "psr-4": {
      "Controller\\": "controller/",
      "Model\\": "model/",
      "Service\\": "service/",
      "Test\\": "test/",
    }
  },
}

最後執行 composer dumpautoload -o 讓自動加載生效。composer

到這裏咱們的安裝就算結束了。若是你使用 phpstorm 進行開發,那麼你須要進行以下的配置:phpstorm

v2-4cee12287e4c9c76237109112d55c3b1_720w.jpg

這裏指明瞭從哪裏加載 PHPUnit,因爲咱們使用 composer 安裝,因此,這裏的文件選擇 composer 生成的 autoload.php 文件便可。函數

使用

好了,假設咱們如今進行開發,在 service 目錄中添加了一個 CalculateService 的文件,而且編寫了一個 abs 的函數。工具

namespace Service;

class CalculateService
{
    public function abs($num)
    {
        return abs($num);
    }
}

如今咱們對 abs 函數進行單元測試,PHPUnit 規定了一個測試類必須遵照以下的規定:
單元測試類名必須以 Test 結尾,必須繼承 \PHPUnit\Framework\TestCase 基類。
每一個測試函數必須以 test 開頭。單元測試

上面的規定是必須遵照的,若是代碼沒有遵照規定 PHPUnit 不會把他當作單元測試代碼。測試

除了以上的兩條,還有一些良好的編碼習慣能夠參考:

單元測試代碼都放在 test 目錄下。
每一個單元測試類以被測試的類名開頭。例如被測試類爲 CalculateService,那麼單元測試類應該爲 CalculateServiceTest。
每一個單元測試函數應該爲被測試函數名結尾。例如被測試函數爲 abs,那麼單元測試函數應該爲 testAbs。

根據上面的規範,編寫單元測試代碼

class UserServiceTest extends \PHPUnit\Framework\TestCase
{
    public function testAbs()
    {
        $userService = new \Service\CalculateService();
        $this->assertEquals(4, $userService->abs(4));
    }
}

在上面的測試代碼中,調用了咱們要測試的函數 abs,而後斷言 $userService->abs(4) 的結果爲 4。在 phpstorm 中直接在 testAbs 函數處右鍵選擇 run UserServiceTest 執行:

v2-1b529d1305b799abd5eed574bda25da1_720w.jpg

發如今控制檯會輸出以下內容

Time: 17 ms, Memory: 4.00MB

OK (1 test, 1 assertion)

代表 abs 經過了 $userService->abs(4) == 4 的測試用例。這裏注意一點,這裏並不代表 abs 函數已經經過測試,一個良好的測試應該包含多個測試用例來覆蓋儘量多的可能性。

如今 PHPUnit 基本的單元測試已經運行成功了,在 [PHPUnit 的文檔]中,有更多關於測試的用法。因爲 PHPUnit 的用法過多,這裏不能一一說明,這裏提一些其餘用法。

PHPUnit 提供了 @test 的註解,若是一個測試函數添加了 @test 註解,那麼測試函數名字就沒必要以 test 開頭。
\PHPUnit\Framework\TestCase 有一個 setUp 函數,若是本身編寫的測試類重寫了這個函數,那麼每次在開始執行測試函數以前,會先執行 setUp 進行測試以前的初始化。一樣,也有一個 tearDown 的函數,若是重寫,那麼在測試函數執行完畢以後調用 tearDown 函數。
.... 更多的內容需參考 PHPUnit 的文檔。

phpunit.xml 文件

在上面的例子中,咱們使用 phpstorm 逐個執行測試函數,可是若是咱們須要一次性執行全部的單元測試,那麼咱們能夠編寫 phpunit.xml 文件來實現。

給出一個 phpunit.xml 的編寫例子來說解 phpunit.xml 的做用

<?xml version="1.0" encoding="UTF-8"?>
<phpunit>
    <testsuites>
        <testsuite>
            <directory>test</directory>
        </testsuite>
    </testsuites>
</phpunit>

這裏 <directory>test</directory> 指定了測試代碼都放在 test 目錄下,在 phpstorm 下右鍵點擊 phpunit.xml 文件選擇 Run phpunit.xml,phpunit 就會到 test 目錄下查找全部單元測試並逐個執行。

除了使用 phpunit.xml 來一次性執行全部的單元測試,還能夠在 phpunit.xml 中配置單元測試結果的輸出日誌。

<?xml version="1.0" encoding="UTF-8"?>
<phpunit>
    .....
    <logging>
        <log type="testdox-html" target="tmp/log.html"/>
    </logging>
</phpunit>

此時在執行 phpunit.xml 文件,就會在項目目錄下生成一個 tmp/log.html 文件,這個文件記錄了全部單元測試的結果。

固然,更多 phpunit.xml 配置相關的內容,仍是須要查看文檔。

Mock 測試

PHPUnit 還提供了 Mock 測試。這裏先介紹一下什麼是 Mock 測試。

假設 foo 函數調用了 bar 函數,那麼在對 foo 函數進行單元測試會有兩個問題:

foo 函數依賴於 bar 函數的結果,那麼在對 foo 進行單元測試的時候必然會引入 bar ,那麼這樣子單元測試就沒意義了,若是測試不經過,那麼沒法保證 bug 出在 foo 仍是 bar。
bar 函數可能在測試環境不可執行,那麼 foo 沒法獲取 bar 的執行結果,從而沒法對 foo 進行單元測試。

Mock 測試就是爲了解決上面的問題而出現的,使用 Mock 咱們能夠虛擬出一個 bar 的調用,而且假設 bar 調用返回結果。若是仍是聽不懂,上一段代碼就知道了。

class MockTest extends \PHPUnit\Framework\TestCase {
    public function testGet()
    {  
        $stub = $this->createMock(\App\UserService::class);     //1
        $stub->method('get')->willReturn(3);                     //2
        $this->assertEquals(3,$stub->get(1));                      //3
    } 
}

上面的測試函數就使用到了 Mock,一行一行代碼來分析:

第一行建立了一個虛擬的 UserService 對象。
第二行假設 UserService 中的 get 函數的返回值爲 3。
第三行調用 $stub->get(1) 不會真的去執行 get 函數,而是根據第二行的 willReturn 函數直接返回 3。

以上就是一個簡單的 Mock 測試,固然 Mock 測試還有不少複雜的用法,這裏沒辦法一一展開,其實掌握基本的用法,更多複雜的高級用法在實踐中碰到了再去查看文檔也不遲。

好了,PHPUnit 的基本操做就這些了,單元測試自己並非一個很難的東西,阻礙單元測試的進行並非在技術上,更多的是一個項目時間安排的衡量與考慮。

attachments-2020-06-6Uw2mdVm5eddd3cbb1b3d.jpg

相關文章
相關標籤/搜索