本文首發於 https://jaychen.cc/article/34php
做者 Jaychenhtml
朋友,你據說過安。。。不是,寫過單元測試嗎。json
單元測試是開發過程當中必不可少的一環,一個項目有良好的單元測試代碼,重構的勇氣都大不少。此次寫一篇小文來介紹一下 PHP 的單元測試工具 PHPUnit 的使用。bash
PHPUnit 的使用並不難,這篇文章主要仍是充當一個引子,介紹基本概念和使用,有了這篇文章的基礎以後,去看官網的文檔就會更加順風順水。composer
安裝 PHPUnit 的方式很簡單,使用 composer 能夠一行代碼就能夠安裝。phpstorm
composer require --dev phpunit/phpunit
複製代碼
安裝以後,在 vendor/bin
目錄下有一個 phpunit 的可執行文件,這個就是 phpunit 本體了。假設咱們項目的目錄結構以下:函數
➜ phpunit tree .
├── controller
├── model
├── service
├── test
└── vendor
├── composer.json
複製代碼
其中咱們的單元測試代碼都放在 test 目錄下。使用 composer 來爲咱們解決 autoload 的問題。工具
{
"autoload": {
"psr-4": {
"Controller\\": "controller/",
"Model\\": "model/",
"Service\\": "service/",
"Test\\": "test/",
}
},
}
複製代碼
若是你還不懂 composer 自動加載的使用,能夠參考這篇文章。最後執行 composer dumpautoload -o
讓自動加載生效。單元測試
到這裏咱們的安裝就算結束了。若是你使用 phpstorm 進行開發,那麼你須要進行以下的配置:測試
這裏指明瞭從哪裏加載 PHPUnit,因爲咱們使用 composer 安裝,因此,這裏的文件選擇 composer 生成的 autoload.php 文件便可。
好了,假設咱們如今進行開發,在 service 目錄中添加了一個 CalculateService
的文件,而且編寫了一個 abs 的函數。
namespace Service;
class CalculateService {
public function abs($num) {
return abs($num);
}
}
複製代碼
如今咱們對 abs
函數進行單元測試,PHPUnit 規定了一個測試類必須遵照以下的規定:
\PHPUnit\Framework\TestCase
基類。上面的規定是必須遵照的,若是代碼沒有遵照規定 PHPUnit 不會把他當作單元測試代碼。除了以上的兩條,還有一些良好的編碼習慣能夠參考:
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
執行:
發如今控制檯會輸出以下內容
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 的文檔。
在上面的例子中,咱們使用 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 配置相關的內容,仍是須要查看文檔。 :laughing:
PHPUnit 還提供了 Mock 測試。這裏先介紹一下什麼是 Mock 測試。
假設 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 的基本操做就這些了,單元測試自己並非一個很難的東西,阻礙單元測試的進行並非在技術上,更多的是一個項目時間安排的衡量與考慮。