本教程所有的PHPUnit測試基於PHPUnit6.5.9
版本,Lumen 5.5
框架
前置
日常我們的普通用到的測試:
- 代碼直接echo,debug等方法測試 -> 跟蹤細節斷點型測試
- log日誌輔助測試 -> 跟蹤細節斷點型測試
- 輔助工具,postman之類的做請求類測試->請求類測試
- 瀏覽器直接測試->瀏覽器測試
單元測試
單元測試是針對程序的最小單元來進行正確性檢驗的測試工作,程序單元就是應用的最小可測試部件,一個單元可能是單個程序,類,對象,方法等
單元測試是用來測試包或者程序的一部分代碼或者一組代碼的函數。測試的目的是確認目標代碼在給定的場景下,有沒有按照期望工作。
一個場景是正向路經測試,就是在正常執行的情況下,保證代碼不產生錯誤的測試。這種測試可以用來確認代碼可以成功地向數據庫中插入一條工作記錄。
另外一些單元測試可能會測試負向路徑的場景,保證代碼不僅會產生錯誤,而且是預期的錯誤。
這種場景下的測試可能是對數據庫進行查詢時沒有找到任何結果,或者對數據庫做了無效的更新。
在這兩種情況下,測試都要驗證確實產生了錯誤,且產生的是預期的錯誤。總之,不管如何調用或者執行代碼,所寫的代碼行爲都是可預期的
優點或改善解決問題
-
減少bug
通過運行單元測試可以直接測試各個功能的正確性,有bug可以直接發現並解決,如果要等到跟其他的功能對接,進行連貫測試,測試比較麻煩,而且bug不能及早的發現並解決
-
快速定位bug
如果是web項目的某一個功能,平常我們定位bug可能是頁面輸入值,後臺斷點,一步一步的需要bug位置,如果有編寫單元測試,則可以直接修改數據,運行單元測試即可,快速有限
-
提高代碼質量
如果每一個部件都是完美的,那麼組合起來肯定也是完美的。整體代碼質量就得到了保障
-
減少調試時間
當不知問題所在的時候,可能需要各種調試與運行,而如果所有的都有編寫單元測試,那麼可以直接運行單元測試,就能定位問題所在位置。
PHPUnit
PHPUnit是一個面向PHP程序員的測試框架,這是一個xUnit的體系結構的單元測試框架。
版本
主版本 | 初始版本 | PHP兼容性 | 支持 | 後臺框架對應版本 |
---|---|---|---|---|
PHPUnit 8 | 2019年2月1日 | PHP 7.2, PHP 7.3, PHP 7.4 | 在2021年2月5日結束支持 | |
PHPUnit 7 | 2018年2月2日 | PHP 7.1, PHP 7.2, PHP 7.3 | 在2020年2月7日結束支持 | |
PHPUnit 6 | 2017年2月3日 | PHP 7.0, PHP 7.1, PHP 7.2 | 在2019年2月1日結束支持 | * |
PHPUnit 5 | 2015年10月2日 | PHP 5.6, PHP 7.0, PHP 7.1 | 在2018年2月2日結束支持 | |
PHPUnit 4 | 2014年3月7日 | PHP 5.3, PHP 5.4, PHP 5.5, PHP 5.6 | 在2017年2月3日結束支持 |
PHPUnit測試一個文件類的生命週期
流程測試代碼
自動加載文件
<?php
/**
* 測試框架的自動加載測試文件類
* User: qikailin
*/
error_reporting(E_ALL ^ E_NOTICE);
require __DIR__ . '/../vendor/autoload.php';
define('WPT_TEST_DIR_BASE', realpath(dirname(__FILE__)));
set_include_path(implode(PATH_SEPARATOR, array(
WPT_TEST_DIR_BASE,
get_include_path()
)));
spl_autoload_register(function ($class) {
$classFile = WPT_TEST_DIR_BASE . DIRECTORY_SEPARATOR . str_replace(["Test\\", "/", "\\"],
["", DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $class) . ".php";
if (file_exists($classFile)) {
include_once $classFile;
}
}, true, false);
PipeTest 流程代碼
<?php
/**
* 測試類的每個測試方法都會運行一次 setUp() 和 tearDown() 模板方法(同時,每個測試方法都是在一個全新的測試類實例上運行的)。
* 另外,setUpBeforeClass() 與 tearDownAfterClass() 模板方法將分別在測試用例類的第一個測試運行之前和測試用例類的最後一個測試運行之後調用。
* 如果有需要共享的對象或變量,可以放在setUpBeforeClass,並設置爲靜態屬性
* User: qikailin
*/
namespace Test\Cases\Headline;
use Test\BaseCase;
class PipeTest extends BaseCase
{
public static function setUpBeforeClass()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
public function setUp()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
/**
* 測試方法的前置執行,setUp之後
*/
protected function assertPreConditions()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
public function testOne()
{
fwrite(STDOUT, __METHOD__ . "\n");
$this->assertTrue(true);
}
public function testTwo()
{
fwrite(STDOUT, __METHOD__ . "\n");
// 兩個交換下順序可以看下效果
// 正常執行成功assert可以繼續執行,失敗的會跳出方法
$this->assertArrayHasKey('d', ['d'=>1, 'e'=>2]);
$this->assertTrue(false);
}
public function testThree()
{
fwrite(STDOUT, __METHOD__ . "\n");
$this->assertTrue(false);
}
public function testFour()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
/**
* 測試方法成功後的後置執行,tearDown之前
*/
protected function assertPostConditions()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
public function tearDown()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
public static function tearDownAfterClass()
{
fwrite(STDOUT, __METHOD__ . "\n");
}
/**
* 不成功後攔截方法
* 必須重新拋出錯誤,如果不拋出錯誤,斷言會當成成功了
*/
public function onNotSuccessfulTest(\Throwable $e)
{
fwrite(STDOUT, __METHOD__ . "\n");
// 必須重新拋出錯誤,如果不拋出錯誤,斷言會當成成功了
throw $e;
}
}
運行
# 你可以把vendor/bin加入到環境變量PATH
../../../vendor/bin/phpunit --bootstrap Headline\
輸出標識說明
.
當測試成功時輸出。
F
當測試方法運行過程中一個斷言失敗時輸出。
E
當測試方法運行過程中產生一個錯誤時輸出。
R
當測試被標記爲有風險時輸出。
S
當測試被跳過時輸出。
I
當測試被標記爲不完整或未實現時輸出。
輸出
Test\Cases\Headline\PipeTest::setUpBeforeClass
Test\Cases\Headline\PipeTest::setUp
Test\Cases\Headline\PipeTest::assertPreConditions
Test\Cases\Headline\PipeTest::testOne
Test\Cases\Headline\PipeTest::assertPostConditions
Test\Cases\Headline\PipeTest::tearDown
Test\Cases\Headline\PipeTest::setUp
Test\Cases\Headline\PipeTest::assertPreConditions
Test\Cases\Headline\PipeTest::testTwo
Test\Cases\Headline\PipeTest::tearDown
Test\Cases\Headline\PipeTest::onNotSuccessfulTest
Test\Cases\Headline\PipeTest::setUp
Test\Cases\Headline\PipeTest::assertPreConditions
Test\Cases\Headline\PipeTest::testThree
Test\Cases\Headline\PipeTest::tearDown
Test\Cases\Headline\PipeTest::onNotSuccessfulTest
Test\Cases\Headline\PipeTest::setUp
Test\Cases\Headline\PipeTest::assertPreConditions
Test\Cases\Headline\PipeTest::testFour
Test\Cases\Headline\PipeTest::assertPostConditions
Test\Cases\Headline\PipeTest::tearDown
Test\Cases\Headline\PipeTest::tearDownAfterClass