摘要:上面的規(guī)定是必須遵守的,如果代碼沒有遵守規(guī)定不會把他當做單元測試代碼。每個單元測試類以被測試的類名開頭。每個單元測試函數(shù)應(yīng)該為被測試函數(shù)名結(jié)尾。函數(shù)可能在測試環(huán)境不可執(zhí)行,那么無法獲取的執(zhí)行結(jié)果,從而無法對進行單元測試。
本文首發(fā)于 https://jaychen.cc/article/34
作者 Jaychen
朋友,你聽說過安。。。不是,寫過單元測試嗎。
單元測試是開發(fā)過程中必不可少的一環(huán),一個項目有良好的單元測試代碼,重構(gòu)的勇氣都大很多。這次寫一篇小文來介紹一下 PHP 的單元測試工具 PHPUnit 的使用。
PHPUnit 的使用并不難,這篇文章主要還是充當一個引子,介紹基本概念和使用,有了這篇文章的基礎(chǔ)之后,去看官網(wǎng)的文檔就會更加順風(fēng)順水。安裝
安裝 PHPUnit 的方式很簡單,使用 composer 可以一行代碼就可以安裝。
composer require --dev phpunit/phpunit
安裝之后,在 vendor/bin 目錄下有一個 phpunit 的可執(zhí)行文件,這個就是 phpunit 本體了。假設(shè)我們項目的目錄結(jié)構(gòu)如下:
? phpunit tree . ├── controller ├── model ├── service ├── test └── vendor ├── composer.json
其中我們的單元測試代碼都放在 test 目錄下。使用 composer 來為我們解決 autoload 的問題。
{ "autoload": { "psr-4": { "Controller": "controller/", "Model": "model/", "Service": "service/", "Test": "test/", } }, }
如果你還不懂 composer 自動加載的使用,可以參考這篇文章。最后執(zhí)行 composer dumpautoload -o 讓自動加載生效。
到這里我們的安裝就算結(jié)束了。如果你使用 phpstorm 進行開發(fā),那么你需要進行如下的配置:
這里指明了從哪里加載 PHPUnit,由于我們使用 composer 安裝,所以,這里的文件選擇 composer 生成的 autoload.php 文件即可。
使用好了,假設(shè)我們現(xiàn)在進行開發(fā),在 service 目錄中添加了一個 CalculateService 的文件,并且編寫了一個 abs 的函數(shù)。
namespace Service; class CalculateService { public function abs($num) { return abs($num); } }
現(xiàn)在我們對 abs 函數(shù)進行單元測試,PHPUnit 規(guī)定了一個測試類必須遵守如下的規(guī)定:
單元測試類名必須以 Test 結(jié)尾,必須繼承 PHPUnitFrameworkTestCase 基類。
每個測試函數(shù)必須以 test 開頭。
上面的規(guī)定是必須遵守的,如果代碼沒有遵守規(guī)定 PHPUnit 不會把他當做單元測試代碼。除了以上的兩條,還有一些良好的編碼習(xí)慣可以參考:
單元測試代碼都放在 test 目錄下。
每個單元測試類以被測試的類名開頭。例如被測試類為 CalculateService,那么單元測試類應(yīng)該為 CalculateServiceTest。
每個單元測試函數(shù)應(yīng)該為被測試函數(shù)名結(jié)尾。例如被測試函數(shù)為 abs,那么單元測試函數(shù)應(yīng)該為 testAbs。
根據(jù)上面的規(guī)范,編寫單元測試代碼
class UserServiceTest extends PHPUnitFrameworkTestCase { public function testAbs() { $userService = new ServiceCalculateService(); $this->assertEquals(4, $userService->abs(4)); } }
在上面的測試代碼中,調(diào)用了我們要測試的函數(shù) abs,然后斷言 $userService->abs(4) 的結(jié)果為 4。在 phpstorm 中直接在 testAbs 函數(shù)處右鍵選擇 run UserServiceTest 執(zhí)行:
發(fā)現(xiàn)在控制臺會輸出如下內(nèi)容
Time: 17 ms, Memory: 4.00MB OK (1 test, 1 assertion)
表明 abs 通過了 $userService->abs(4) == 4 的測試用例。這里注意一點,這里并不表明 abs 函數(shù)已經(jīng)通過測試,一個良好的測試應(yīng)該包含多個測試用例來覆蓋盡可能多的可能性。
現(xiàn)在 PHPUnit 基本的單元測試已經(jīng)運行成功了,在 PHPUnit 的文檔中,有更多關(guān)于測試的用法。由于 PHPUnit 的用法過多,這里不能一一說明,這里提一些其他用法。
PHPUnit 提供了 @test 的注解,如果一個測試函數(shù)添加了 @test 注解,那么測試函數(shù)名字就不必以 test 開頭。
PHPUnitFrameworkTestCase 有一個 setUp 函數(shù),如果自己編寫的測試類重寫了這個函數(shù),那么每次在開始執(zhí)行測試函數(shù)之前,會先執(zhí)行 setUp 進行測試之前的初始化。同樣,也有一個 tearDown 的函數(shù),如果重寫,那么在測試函數(shù)執(zhí)行完畢之后調(diào)用 tearDown 函數(shù)。
.... 更多的內(nèi)容需參考 PHPUnit 的文檔。
phpunit.xml 文件在上面的例子中,我們使用 phpstorm 逐個執(zhí)行測試函數(shù),但是如果我們需要一次性執(zhí)行所有的單元測試,那么我們可以編寫 phpunit.xml 文件來實現(xiàn)。
給出一個 phpunit.xml 的編寫例子來講解 phpunit.xml 的作用
test
這里
除了使用 phpunit.xml 來一次性執(zhí)行所有的單元測試,還可以在 phpunit.xml 中配置單元測試結(jié)果的輸出日志。
.....
此時在執(zhí)行 phpunit.xml 文件,就會在項目目錄下生成一個 tmp/log.html 文件,這個文件記錄了所有單元測試的結(jié)果。
當然,,,更多 phpunit.xml 配置相關(guān)的內(nèi)容,還是需要查看文檔。
Mock 測試PHPUnit 還提供了 Mock 測試。這里先介紹一下什么是 Mock 測試。
假設(shè) foo 函數(shù)調(diào)用了 bar 函數(shù),那么在對 foo 函數(shù)進行單元測試會有兩個問題:
foo 函數(shù)依賴于 bar 函數(shù)的結(jié)果,那么在對 foo 進行單元測試的時候必然會引入 bar ,那么這樣子單元測試就沒意義了,如果測試不通過,那么無法保證 bug 出在 foo 還是 bar。
bar 函數(shù)可能在測試環(huán)境不可執(zhí)行,那么 foo 無法獲取 bar 的執(zhí)行結(jié)果,從而無法對 foo 進行單元測試。
Mock 測試就是為了解決上面的問題而出現(xiàn)的,使用 Mock 我們可以虛擬出一個 bar 的調(diào)用,并且假設(shè) bar 調(diào)用返回結(jié)果。如果還是聽不懂,上一段代碼就知道了。
class MockTest extends PHPUnitFrameworkTestCase { public function testGet() { $stub = $this->createMock(AppUserService::class); //1 $stub->method("get")->willReturn(3); //2 $this->assertEquals(3,$stub->get(1)); //3 } }
上面的測試函數(shù)就使用到了 Mock,一行一行代碼來分析:
第一行創(chuàng)建了一個虛擬的 UserService 對象。
第二行假設(shè) UserService 中的 get 函數(shù)的返回值為 3。
第三行調(diào)用 $stub->get(1) 不會真的去執(zhí)行 get 函數(shù),而是根據(jù)第二行的 willReturn 函數(shù)直接返回 3。
以上就是一個簡單的 Mock 測試,當然 Mock 測試還有很多復(fù)雜的用法,這里沒辦法一一展開,其實掌握基本的用法,更多復(fù)雜的高級用法在實踐中碰到了再去查看文檔也不遲。
好了,PHPUnit 的基本操作就這些了,單元測試本身并不是一個很難的東西,阻礙單元測試的進行并不是在技術(shù)上,更多的是一個項目時間安排的衡量與考慮。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/26256.html
摘要:另外一些單元測試可能會測試負向路徑的場景,保證代碼不僅會產(chǎn)生錯誤,而且是預(yù)期的錯誤。是一個面向程序員的測試框架,這是一個的體系結(jié)構(gòu)的單元測試框架。 本系列教程所有的PHPUnit測試基于PHPUnit6.5.9版本,Lumen 5.5框架 前置 日常我們的普通用到的測試: 代碼直接echo,debug等方法測試 -> 跟蹤細節(jié)斷點型測試 log日志輔助測試 -> 跟蹤細節(jié)斷點型測試 ...
摘要:另外一些單元測試可能會測試負向路徑的場景,保證代碼不僅會產(chǎn)生錯誤,而且是預(yù)期的錯誤。是一個面向程序員的測試框架,這是一個的體系結(jié)構(gòu)的單元測試框架。 本系列教程所有的PHPUnit測試基于PHPUnit6.5.9版本,Lumen 5.5框架 前置 日常我們的普通用到的測試: 代碼直接echo,debug等方法測試 -> 跟蹤細節(jié)斷點型測試 log日志輔助測試 -> 跟蹤細節(jié)斷點型測試 ...
摘要:單元測試的好處是給開發(fā)人員的,并不是給機器的。對于查詢構(gòu)造器這個項目,我們可以讓其在遠程運行環(huán)境安裝相關(guān)數(shù)據(jù)庫軟件,執(zhí)行數(shù)據(jù)表建立,數(shù)據(jù)導(dǎo)入,執(zhí)行單元測試等操作。查詢構(gòu)造器的完整代碼查詢構(gòu)造器的單元測試完整代碼。 debug 模式 對查詢構(gòu)造器進行調(diào)試并不難,從其構(gòu)造 SQL -> 數(shù)據(jù)綁定 -> SQL 執(zhí)行的過程中就能發(fā)現(xiàn),要方便調(diào)試,只要可以觀察以下信息: 構(gòu)造的 SQL 綁定...
摘要:以前在學(xué)習(xí)開發(fā)時有專門寫過的單元測試的文章,開發(fā)學(xué)習(xí)之單元測試,今天再總結(jié)下怎么在中使用單元測試。是一個用編程語言開發(fā)的開源軟件,是一個單元測試框架。單元測試框架經(jīng)常會包含每個測試的報告,以及給出你已經(jīng)覆蓋到的代碼覆蓋率。 以前在學(xué)習(xí)IOS開發(fā)時有專門寫過Objective-C的單元測試的文章,IOS開發(fā)學(xué)習(xí)之單元測試,今天再總結(jié)下怎么在PHP中使用單元測試。 一、前言 在這篇文章中,...
摘要:只實例化一次,供測試的清理和裝載基境使用對于每個測試,只實例化一次至今為止,完成了最基礎(chǔ)和入門的單元測試和數(shù)據(jù)庫測試,最終數(shù)據(jù)庫無非就是查看數(shù)據(jù)增刪改查是否和預(yù)期一樣。恭喜你,你已經(jīng)構(gòu)建完自己的單元測試環(huán)境了。 我總感覺 PHP 的開發(fā)者們并沒有對 PHP 的質(zhì)量有所追求,可能是因為 PHP 的機制問題吧,讓大部分的開發(fā)者總以為瀏覽器訪問就沒有問題,所以很多時候,做 PHP 開發(fā)的,就...
閱讀 3652·2021-09-02 15:11
閱讀 4563·2021-08-16 10:47
閱讀 1560·2019-08-29 18:35
閱讀 3030·2019-08-28 17:54
閱讀 2843·2019-08-26 11:37
閱讀 1496·2019-08-23 16:51
閱讀 1799·2019-08-23 14:36
閱讀 1801·2019-08-23 14:21