PHPUnit 手冊「筆記」

一、安裝

composer require --dev phpunit/phpunit ^6.5

composer require --dev phpunit/dbunit

二、編寫測試

A.PHPUnit編寫測試

1.基本慣例與步驟:

* 針對類Class的測試寫在類ClassTest中

* ClassTest(通常)繼承自PHPUnit\Framework\TestCase

* 測試都是命名為test*的公用方法,也可以在方法的文檔註釋塊(docblock)中使用@test標註將其標記為測試方法

* 在測試方法內,類似於assertEquals()這樣的斷言方法用來對實際值與預期值的匹配做出斷言

2.當你想把一些東西寫到print語句或者調試表達式中時,別這麼做,將其寫成一個測試來代替

StackTest.php

B.測試的依賴關係

1.單元測試主要是作為一種良好實踐來編寫的,它能幫助開發人員識別並修復 bug、重構代碼,還可以看作被測軟件單元的文檔。要實現這些好處,理想的單元測試應當覆蓋程序中所有可能的路徑。一個單元測試通常覆蓋一個函數或方法中的一個特定路徑。但是,測試方法並不一定非要是一個封裝良好的獨立實體。測試方法之間經常有隱含的依賴關係暗藏在測試的實現方案中

2.PHPUnit支持對測試方法之間的顯式依賴關係進行聲明。這種依賴關係並不是定義在測試方法的執行順序中,而是允許生產者(producer)返回一個測試基境(fixture)的實例,並將此實例傳遞給依賴於它的消費者(consumer)們

* 生產者,是能生成被測單元將其作為返回值的測試方法

* 消費者,是依賴於一個或多個生產者及其返回值的測試方法

3.使用@depends標註來表達測試方法之間的依賴關係,如果需要傳遞對象副本而非引用,則應當用@depends clone替代@depends

4.測試可以使用多個@depends標註,需要保證某個測試所依賴的所有測試均出現於這個測試之前

5.擁有多個@depends標註的測試,其第一個參數是每一個生產者提供的基境,第二個參數是第二個生產者提供的基境,以此類推

MultipleDependenciesTest.php、DependencyFailureTest.php、DependencyAndDataProviderComboTest.php

C.數據供給器

1.測試方法可以接受任意參數。這些參數由數據供給器方法提供。用@dataProvider標註來指定使用哪個數據供給器方法

2.數據供給器方法必須聲明為public,其返回值要麼是一個數組,其每個元素也是數組;要麼是一個實現了Iterator接口的對象。每個數組都是測試數據集的一部分,將以它的內容作為參數來調用測試方法

3.當使用到大量數據集時,最好逐個用字符串鍵名對其命名,避免用默認的數字鍵名,這樣輸出的信息會更加詳細些

4.如果測試同時從@dataProvider方法和一個或多個@depends測試接收數據,那麼來自於數據供給器的參數將先於來自所依賴的測試參數

5.如果一個測試依賴於另一個使用了數據供給器的測試,僅當被依賴的測試至少能在一組數據上成功時,依賴於它的測試才會運行。使用了數據供給器的測試,其運行結果是無法注入到依賴於此測試的其他測試中的

6.所有的數據供給器方法的執行都是在對setUpBeforeClass靜態方法的調用和第一次對setUp方法的調用之前完成的。因此,無法在數據供給器中使用創建於這兩個方法內的變量。這樣PHPUnit才能計算測試的總數量。

DataTest.php

D.對異常進行測試

1.使用expectException()、expectExceptionCode()、expectExceptionMessage()、expectExceptionMessageRegExp()方法可以為被測代碼所拋出的異常建立預期

2.也可以用@expectException、@expectExceptionCode、@expectExceptionMessage、@expectExceptionMessageRegExp標註

ExceptionTest.php、ExpectedErrorTest.php

E.對PHP錯誤進行測試

1.默認情況下PHPUnit將測試在執行中觸發的PHP錯誤、警告、通知都轉換為異常

2.PHP的error_reporting運行時配置會對PHPUnit將哪些錯誤轉換為異常有所限制

3.對異常進行測試是越明確越好,對太籠統的類進行測試有可能導致不良副作用

4.如果測試依靠會觸發錯誤的PHP函數,例如fopen,有時候在測試中使用錯誤抑制符會很有用。通過抑制住錯誤通知,就能對返回值進行檢查,否則會導致拋出異常

ErrorSuppressionTest.php

F.對輸出進行測試

1.有時候,想要斷言(比如說)某方法的運行過程中生成了預期的輸出(通過echo或print)。PHPUnit\Framework\TestCase類使用PHP的輸出緩衝特性來為此提供必要的功能支持

2.使用expectOutputString()方法來設定所預期的輸出,如果沒有產生預期的輸出,測試將計為失敗

3.輸出進行測試的方法

* expectOutputRegex(string $regularExpression)設置輸出預期為輸出應當匹配正則表達式

* expectOutputString(string @expectedString)設置輸出預期為輸出應當與$expectedString字符串相等

* setOutputCallback(callable $callback)設置回調函數,用來做諸如將實際輸出規範化之類的動作

* string getActualOutpu()獲取實際輸出

4.嚴格模式下本身產生輸出的測試將會失敗

OutputTest.php

G.錯誤相關信息的輸出

1.當有測試失敗時,PHPUnit全力提供儘可能多的有助於找出問題所在的上下文信息

2.當生成的輸出很長而難以閱讀時,PHPUnit將對其進行分割,並在每個差異附近提供少數幾行上下文信息

三、命令行測試執行器

1.對於每個測試的運行,PHPUint命令行工具輸出一個字符來指示進展:

【.】當測試成功時輸出

【F】當測試方法運行過程中一個斷言失敗時輸出

【E】當測試方法運行過程中產生一個錯誤時輸出

【R】當測試被標記為有風險時輸出

【S】當測試被跳過時輸出

【I】當測試被標記為不完整或未實現時輸出

2.PHPUnit區分失敗(failure)與錯誤(error),失敗是違背了PHPUnit斷言,錯誤是意料之外的異常,錯誤往往比失敗更容易修復

A.命令行選項

* -h|--help,幫助

* UnitTest,運行由UnitTest類提供的測試

* --coverage-clover,為運行的測試生成帶有代碼覆蓋率信息的XML格式的日誌文件,僅當安裝了tokenizer和Xdebug這兩個PHP擴展後才可用

* --coverage-crap4j,生成Crap4j格式的代碼覆蓋率報告,僅當安裝了tokenizer和Xdebug這兩個PHP擴展後才可用

* --coverage-html,生成HTML格式的代碼覆蓋率報告,僅當安裝了tokenizer和Xdebug這兩個PHP擴展後才可用

* --coverage-php,生成一個序列化後的PHP_CodeCoverage對象,此對象含有代碼覆蓋率信息,僅當安裝了tokenizer和Xdebug這兩個PHP擴展後才可用

* --coverage-text,為運行的測試以人們可讀的格式生成帶有代碼覆蓋率信息的日誌文件或命令行輸出,僅當安裝了tokenizer和Xdebug這兩個PHP擴展後才可用

* --log-junit,為運行的測試生成JUnit XML格式的日誌文件

* --testdox-html和--testdox-text,為運行的測試以HTML或純文本格式生成敏捷文檔

* --filter,只運行與給定模式匹配的測試

* --testsuite,只運行名稱與給定模式匹配的測試套件

* --group,只運行來自指定分組(可以多個)的測試。可以用@group標註為測試標記其所屬的分組,@author標註是@group的一個別名,允許按作者來篩選測試

* --exclude-group,排除來自指定分組的測試

* --list-groups,列出所有有效的測試分組

* --test-suffix,只查找文件名以指定後綴(可以多個)結尾的測試文件

* --report-useless-tests,更嚴格對待事實上不測試任何內容的測試

* --strict-global-state,更嚴格對待全局狀態篡改

* --strict-coverage,更嚴格對待意外的代碼覆蓋

* --disallow-test-output,更嚴格對待測試執行期間產生的輸出

* --disallow-todo-tests,不執行文檔註釋塊中含有@todo標註的測試

* --enforce-time-limit,根據測試規模對其加上執行時長限制

* --process-isolation,每個測試都在獨立的PHP進程中運行

* --no-globals-backup,不要備份並還原$GLOBALS

* --static-backup,備份並還原用戶定義的類中的靜態屬性

* --colors,使用彩色輸出,三個值:never完全不使用,auto當前終端默認,always總是彩色輸出

* --columns,定義輸出所使用的列數

* --stderr,選擇輸出到STDERR而非STDOUT

* --stop-on-error,首次錯誤出現後停止執行

* --stop-on-failure,首次錯誤或失敗後停止執行

* --stop-on-risky,首次踫到有風險的測試時停止執行

* --stop-on-skipped,首次碰到到跳過的測試時停止執行

* --stop-on-incomplete,首次碰到不完整的測試時停止執行

* --verbose,輸出更詳盡的信息,如不完整或跳過的測試的名稱

* --debug,輸出調試信息,如當一個測試開始執行時輸出其名稱

* --loader,指定要使用的PHPUnit_Runner_TestSuiteLoader實現

* --repeat,將測試重複運行指定次數

* --testdox,將測試進度以敏捷文檔方式報告

* --printer,指定要使用的結果輸出器(printer)

* --bootstrap,在測試前先運行一個“bootstrap”PHP文件

* --configuration,-c,從XML文件中讀取配置信息

* --no-configuration,忽略當前工作目錄下的phpunit.xml與phpunit.xml.dist

* --include-path,向PHP的include_path開頭添加指定路徑(可以多個)

* -d,設置指定的PHP配置選項的值

四、基境(fixture)

1.在編寫測試時,最費時的部分之一是編寫代碼來將整個場景設置成某個已知的狀態,並在測試結束後將其復原到初始狀態,這個已知的狀態稱為測試的基境(fixture)

2.PHPUnit支持共享建立基境的代碼,在運行某個測試方法前,會調用一個名叫setUp()的模板方法,setUp()是創建測試所用對象的方法,當測試方法運行結束後,不管成功還是失敗,都會調用另外一個名叫tearDown()的模板方法,清理測試所有對象的方法

3.測試類的每個測試方法都會運行一次setUp()和tearDown()模板方法,setUpBeforeClass()和tearDownAfterClass()模板方法將分別在測試用例類的第一個測試運行之前和測試用例類的最後一個測試運行之後調用

4.在setUp()中分配了諸如文件或套接字之類的外部資源時才需要實現tearDown(),如果setUp()中只創建純PHP對象,通常可以忽略tearDown()

5.如果兩個setUp()代碼有微小差異,把有差異的內容從setUp()移到測試方法內;如果兩個setUp()是確實不一樣,那麼需要另外一個測試用例類

6.在測試之間共享基境的需求都源於某個未解決的設計問題,有實際意義的多測試間共享基境的例子是數據庫鏈接

7.在測試之間共享基境會降低測試的價值,潛在的設計問題是對象之間並非鬆散耦合

8.使用單件(singleton)的代碼很難測試,使用全局變量的代碼也一樣,代碼與全局變量之間會強烈耦合,一個測試對全局變量的改變可能會影響另一個

9.$backupGlobalsBlacklist,變量可以提供全局變量黑名單;@backupGlobals標註可以用來控制對全局變量的備份與還原操作;@backupStaticAttributes標註可以用於在每個測試之前備份所有已聲明類的靜態屬性值並在其後恢復

StackTest4.php、TemplateMethodsTest.php、Database4_3.php

五、組織測試

A.用文件系統來編排測試套件

1.把所有測試用例源文件放在一個測試目錄中,通過對測試目錄進行遞歸遍歷,PHPUnit能自動發現並運行測試

2.這種方法的缺點是無法控制測試的運行順序,可能導致測試的依賴關係方面的問題

B.用XML配置來編排測試套件

1.如果phpunit.xml或phpunit.xml.dist存在於當前工作目錄並且未使用--configuration,將自動從此文件中讀取配置

/5_1test/phpunit.xml

六、有風險的測試

1.PHPUnit可以更嚴格對待事實上不測試任何內容的測試,可以用命令行--report-useless-tests或在PHPUnit的XML中設置beStrictAboutTestsThatDoNotTestAnything="true"來啟用,如果某個測試未時行任何斷言,它將被標記為有風險

2.可以更嚴格對待意外的代碼覆蓋,用命令行--strict-coverage或在XML配置文件中設置beStrictAboutCoversAnnotation="true"來啟用,如果某個帶有@covers標註的測試執行了未在@covers或@uses標註中列出的代碼,它將被標記為有風險

3.可以更嚴格對待測試執行期間產生的輸出,用命令行--disallow-test-output或在XML中設置beStrictAboutOutputDuringTests ="true"來啟用,如果某個測試產生了輸出,將被標記為有風險

4.測試執行時長的超時限制,如果安裝了PHP_Invoker包並且pcntl擴展可用,可以對測試的執行時長進行限制

5.可以更嚴格的對待篡改全局狀態的測試,用命令行--strict-global-state或在XML中配置beStrictAboutChangesToGlobalState="true"

七、未完成的測試與跳過的測試

A.未完成的測試

1.空測試的問題是PHPUnit框架會將它們解讀為成功

2.PHPUnit_Framework_IncompleteTest是一個標記接口,用於將測試方法拋出的異常標記為測試未完成或目前尚未實現而導致的結果,PHPUnit_Framework_IncompleteTestError是這個接口的標準實現

4.命令行測試執行器中的輸出標記為I

5.用於未完成測試的API,void markTestIncomplete(string $message),將當前測試標記為未完成,並用$message作為說明信息

B.跳過測試

1.並非所有測試都能在任何環境中運行,用markTestSkipped()方法來跳過此測試

2.命令行測試執行器中的輸出標記為S(測試是R)

3.用於跳過測試的API,void markTestSkipped(string $message),將當前測試標記為已跳過,並用$message作為說明信息

C.用@requires來跳過測試

1.可以用@requires標註來跳過測試用例的一些常見前提條件

* @requires PHP 5.3|7.1……,PHP版本

* @requires PHPUnit 3.6.3…… PHPUnit版本

* @requires OS Linux|WIN32|WINNT 系統版本

* @requires function 任何對於 function_exists而言有效的參數

* @requires extension 任何擴展模塊名

SampleTest7_1.php、DatabaseTest7_2.php、DatabaseTest7_3.php

八、數據庫測試

A.數據庫測試的難點

1.需要考慮的變數:

* 數據庫和表

* 向表中插入測試所需要的行

* 測試運行完畢後驗證數據庫的狀態

* 每個新測試都要清理數據庫

2.測試代碼應當儘可能簡短精簡:

* 你不希望因為生產代碼的小變更而需要對測試代碼進行數據可觀的修改

* 你希望在哪怕好幾個月以後也能輕鬆地閱讀並理解測試代碼

3.本質上說,數據庫是全局輸入變量

B.數據庫測試的四個階段

1.單元測試四個階段:

* 建立基境(fixture)

* 執行被測系統

* 驗證結果

* 拆除基境(fixture)

2.數據庫擴展進行測試的流程:

* 清理數據庫:在所有表上執行TRUNCATE操作清空

* 建立基境:將迭代所有指定的基境數據行並將其插入到對應的表裡

* 運行測試

* 驗證結果

* 拆除基境

C.PHPUnit數據庫測試用例的配置

1.如果測試代碼用到了數據庫擴展模塊,需要擴展另一個抽象TestCase(PHPUnit\DbUnit\TestCaseTrait)類,要求實現getConnection()和getDataSet()

2.PHPUnit的數據庫擴展模塊需要用PDO庫來實現跨供應商抽象訪問數據庫連接,PDO連接僅僅用於清理和建立基境

3.getDataSet()方法定義了在每個測試執行之前的數據庫初始狀態應該是什麼樣,數據庫的狀態由PHPUnit_Extensions_Database_DataSet_IDataSet所代表的DataSet數據集和由PHPUnit_Extensions_Database_DataSet_IDataTable所代表的DataTable數據表這兩個概念進行抽象

4.setUp()中會調用一次getDataSet()方法來接收基境數據集並將其插入數據庫

D.理解DataSet(數據集)和DataTable(數據表)

1.DataSet和DataTable是圍繞著數據庫表、行、列的抽象層,通過一套簡單的API,底層數據庫內容被隱藏在對象結構之下,這個對象結構也可以用其他非數據庫數據源來實現

2.預期內容可以用諸如XML、YAML、CSV文件或者PHP數組等方式來表達

3.在測試中,數據庫斷言的工作流由三個步驟組成:

* 用表名稱來指定數據庫中的一個或多個表(實際上是指定了一個數據集)

* 用你喜歡的格式(YAML、XML等等)來指定預期數據集

* 斷言這兩個數據集陳述是彼此相等的

4.數據庫TestCase類強制要求定義一個基境數據集,用它來:

* 根據此數據集所指定的所有表名,將數據庫中對應表內的行全部刪除

* 將數據集內數據表中的所有行寫入數據庫

5.三種不同類型:基於文件的、基於查詢的、篩選與組合

6.Flat XML DataSet(平直XML數據集):

* 一種非常簡單的XML格式,根節點為,根節點下每個標籤代表數據庫中的一行數據,標籤就等於表名,而每一個屬性代表一個列

* 在Flat XML DataSet中,要處理NULL值會非常麻煩,必須保證每個表的第一行不包含NULL值,只有後繼的那些行才能省略屬性,建議只在不需要NULL值的情況下使用Flat XML DataSet

* 使用createFlatXmlDataset()創建實例對象

7.XML DataSet(XML數據集):

* 避免了NULL值問題,在根節點下,可以指定

、column、row、value、null標籤

* 使用createXmlDataSet()創建實例對象

8.MySQL XML DataSet(MySQL XML數據集):

* 可以用mysqldump工具來生成這種模式的文件

* 使用createMySQLXMLDataSet()來創建實例對象

9.YAML DataSet(YAML數據集):沒有工廠方法,需要手動進行實例化

10.CSV DataSet(CSV數據集):無法指定NULL值

11.Array DataSet(數組數據集):可以處理NULL值,不需要為斷言提供額外文件

12.Query(SQL)DataSet(查詢SQL數據集)

13.Database (DB) DataSet(數據庫數據集):通過訪問測試所使用的數據庫鏈接,可以自動創建包含數據庫所有表以及其內容的DataSet

14.Replacement DataSet(替換數據集):是已有數據集的修飾器(decorator),能夠將數據集中任意列的值替換為其他替代值

15.DataSet Filter(數據集篩選器):為需要包含在子數據集中的表和列指定白/黑名單

16.Composite DataSet(組合數據集):能將多個已存在的數據集聚合成單個數據集

17.假如數據庫中使用了外鍵,必須指定好表的順序,避免外鍵約束失敗

E.數據庫連接API

1.getConnection()方法返回的連接接口方法:

* createDataSet()方法創建一個Database (DB) DataSet

* createQueryTable()方法用於創建QueryTable實例,需要為其指定名稱和所使用的SQL查詢,當涉及到結果/表的斷言這個方法會很方便

* getRowCount()提供了一種方便的方式來取得表中的行數,並且還可以選擇附加一個WHERE子句來在計數前對數據行進行過濾

F.數據庫斷言API

1.對錶中數據行的數量作出斷言:$this->getConnection()->getRowCount('guestbook')

2.對查詢的結果作出斷言:assertTablesEqual();

G.常見問題

1.PHPUnit要求測試套件開始時所有數據庫對象必須全部可用,由於每個測試都會徹底清空數據庫,因此無須為每個測試重新創建數據庫

2.只有在基境的清理與建立階段還有斷言檢定時用到PDO

3.如果沒有對TestCase中的getConnection()方法所創建PDO實例進行緩存,那麼每個數據庫測試都會增加一個名多個數據庫連接

MyGuestbookTest8_1.php、MyApp_Tests_DatabaseTestCase8_3.php、GuestbookTest8_3.php、8_1Test/、數組DataSet類

九、測試替身

1.Gerard Meszaros介紹了測試替身的概念:

* 有時候對被測系統(SUT)進行測試是很困難的,因為它依賴於其他無法在測試環境中使用的組件。這有可能是因為這些組件不可用,它們不會返回測試所需要的結果,或者執行它們會有不良副作用。在其他情況下,我們的測試策略要求對被測系統的內部行為有更多控制或更多可見性。

* 如果在編寫測試時無法使用(或選擇不使用)實際的依賴組件(DOC),可以用測試替身來代替。測試替身不需要和真正的依賴組件有完全一樣的行為方式;他只需要提供和真正的組件同樣的API即可,這樣被測系統會以為它是真正的組件!

2.PHPUnit提供的createMock($type)和getMockBuilder($type)方法可以在測試中用來自動生成對象,可以充當任意指定原版類型(接口或類名)的測試替身

3.createMock()方法直接返回指定類型(接口或類)的測試替身實例,替身的創建使用了最佳實踐的默認值(不可執行原始類的__construct()和__clone()方法,且不對傳遞給測試替身的方法的參數進行克隆),如果這些默認值非你所需,可以用getMockBuilder()方法並使用流暢式接口來定製測試替身的生成過程

4.默認情況下,原版類的所有方法都會被替換為只會返回null的偽實現(其中不會調用原版方法)

5.侷限性:final、private與static,無法對其進行上樁(stub)或模仿(mock)

A.Stubs(樁件)

1.將對象替換為(可選地)返回配置好的返回值的測試替身的實踐方法稱為上樁(stubbing)。可以用樁件(stub)來“替換掉被測系統所依賴的實際組件,這樣測試就有了對被測系統的間接輸入的控制點。這使得測試能強制安排被測系統的執行路徑,否則被測系統可能無法執行”

2.僅當原始類中不包含名字為“method”的方法時,才能正常運行,如果包含,就必須用$stub->expects($this->any())->method('doSomething')->willReturn('foo');

3.willReturn($value)返回簡單值,相當於will($this->returnValue($value))

4.有時想要將(未改變的)方法調用時所使用的參數之一作為樁件的方法的調用結果來返回,可以使用returnArgument()

5.在用流暢式接口進行測試時,讓某個已上樁的方法返回對樁件對象的引用有時會很有用,使用returnSelf()

6.有時候,上樁的方法需要根據定義的參數清單來返回不同的值,可以用returnValueMap()方法將參數和相應的返回值關聯起來建立映射

7.如果上樁的方法需要返回計算得到的值而不固定值或某個參數,可以用returnCallback()來讓上樁的方法返回回調函數或方法的結果

8.相比於建立回調方法,更簡單的選擇是直接給出期望返回值的列表,可以用onConsecutiveCalls()方法來做到這個

9.除了返回一個值之外,上樁的方法還能用throwException()拋出一個異常

10.可以自行編寫樁件,被廣泛使用的資源是通過單個外觀(facade)來訪問的,因此很容易就能用樁件替換掉資源

11.需要上樁的功能往往集中在同一個對象中,這就改善了內聚度,將功能通過單一且一致的接口呈現出來,就降低了這部分與系統其他部分之間的耦合度

B.仿件對象(Mock Object)

1.將對象替換為能驗證預期行為(例如斷言某個方法必會被調用)的測試替身的實踐方法稱為模仿(mocking)

2.可以用仿件對象(mock object)“作為觀察點來核實被測系統在測試中的間接輸出。通常,仿件對象還需要包括樁件的功能,因為如果測試尚未失敗則仿件對象需要向被測系統返回一些值,但是其重點還是在對間接輸出的核實上。因此,仿件對象遠不止是樁件加斷言,它是以一種根本上完全不同的方式來使用的”

3.侷限性:對預期的自動校驗,只會對在某個測試的作用域內生成的仿件對象進行自動校驗

4.with()方法可以攜帶任何數量的參數,對應於被模仿的方法的參數數量,可以對方法的參數指定更加高等的約束而不僅是簡單的匹配

5.withConsecutive()方法可以接受任意多個數組作為參數,具體數量取決於欲測試的調用,每個數組都是對被仿方法的相應參數的一組約束,就像with()中那樣

6.callback()約束用來進行更加複雜的參數校驗,此約束的唯一參數是一個PHP回調項(callback),此PHP回調項接受需要校驗的參數作為其唯一參數,並應當在參數通過校驗時返回true,否則返回false

7.匹配器:

* any(),當被評定的方法執行0次或更多次時匹配成功

* never(),當被評定的方法從未執行時匹配成功

* atLeastOnce(),當被評定的方法執行至少一次時匹配成功

* once(),當被評定的方法執行恰好一次時匹配成功

* exactly(int $count),當被評定方法執行恰好$count次時匹配成功

* at(int $index),當被評定的方法是第$index個執行的方法時匹配成功

C.對特質(Trait)與抽象類進行模仿

1.getMockForTrait()方法返回一個使用了特定特質(trait)的仿件對象,給定特質的所有抽象方法將都被模仿

2.getMockForAbstractClass()方法返回一個抽象類的仿件對象,給定抽象類的所有抽象方法都被模仿

D.對Web服務(Web Services)進行上樁或模仿

1.使用getMockFromWsdl(),返回的樁件或者仿件是基於以WSDL描述的web服務

E.對文件系統進行模仿

1.vfsStream是對虛擬文件系統的流包覆器(stream wrapper),可用於模仿真實文件系統,composer安裝:mikey197/vfsStream

2.如果不使用諸如vfsStream這樣的虛擬文件系統,就無法在隔離外部影響的情況下對setDirectory()方法進行測試

SomeClass9_1.php、StubTest9_1.php、SubjectAndObserver9_2.php、SubjectTest9_2.php、TraitClassTest9_3.php、AbstractClassTest9_3.php、Example9_5.php、ExampleTest9_5.php

十、測試實踐

A.在開發過程中

1.當需要對軟件的內部結構進行更改時,實際上是要在不影響其可見行為的情況下讓它更加容易理解、更加易於修改,測試套件對於重構而言是非常寶貴的

2.有助於改進項目的編碼與設計:

* 所有單元測試均正確運行

* 代碼傳達其設計原則

* 代碼沒有冗餘

* 代碼所包含的類和方法的數量降至最低

B.在調試過程中

1.壓住衝動:

* 確認能夠重現此缺陷

* 在代碼中尋找此缺陷的最小規模表達

* 編寫一個目前會失敗而缺陷修復後將會成功的自動測試

* 修復缺陷

2.尋找缺陷的最小可靠重現使你有機會去真正檢查缺陷的原因。當修復了缺陷之後,所編寫的測試則有助於提高缺陷真正被修復的幾率,因為新加入的測試降低了未來修改代碼時又破壞此修復的可能性。而之前所編寫的所有測試則降低了在不經意間導致其他問題的可能性

3.進行單元測試的好處:

* 進行測試讓代碼的作者和評審者對補丁能夠產生正確的結果有信心

* 編寫測試用例對開發者而言是一種很好的發現邊緣情況的原動力

* 進行測試提供了一種良好的方法來快速捕捉退步(Regression),並且能用來保證退步不會重複出現

* 單元測試就如何使用API提供了可正常工作的範例,能夠大大幫助文檔編制工作

十一、代碼覆蓋率分析

1.計算機科學中所說的代碼覆蓋率是一種用於衡量特定測試套件對程序源代碼測試程度的指標。擁有高代碼覆蓋率的程序相較於低代碼低概率的程序而言測試的更加徹底、包含軟件bug的可能性更低

A.用於代碼覆蓋率的軟件衡量標準

1.行覆蓋率(Line Coverage)按單個可執行行是否已執行到進行計量

2.函數與方法覆蓋率(Function and Method Coverage)按單個函數或方法是否已調用到進行計量。僅當函數或方法的所有可執行行全部已覆蓋時PHP_CodeCoverage才將其視為已覆蓋

3.類與物質覆蓋率(Class and Trait Coverage)按單個類或特質的所有方法是否全部已覆蓋進行計量。僅當一個類或性狀的所有方法全部已覆蓋時PHP_CodeCoverage才將其視為已覆蓋

4.Opcode覆蓋率(Opcode Coverage)按函數或方法對應的每條opcode在運行測試套件時是否執行到進行計量,一行代碼通常會編譯得到多條opcode,進行行覆蓋率計量時,只要其中任何一條opcode被執行就視為此行已覆蓋

5.分支覆蓋率(Branch Coverage)按控制結構的分支進行計量,測試套件運行時每個控制結構的布爾表達式求值為true和false各自計為一個分支

6.路徑覆蓋率(Path Coverage)按測試套件運行時函數或者方法內部所經歷的執行路徑進行計量,一個執行路徑指的是從進入函數或方法一直到離開的過程中經過各個分支的特定序列

7.變更風險反模式(CRAP)(Change Risk Anti-Patterns (CRAP) Index)基於代碼單元的圈度(cyclomatic complexity)與代碼覆蓋率計算得出的,不太複雜並具有恰當測試覆蓋率的代碼將得出較低的CRAP指數

B.將文件列入白名單

1.可以用命令行選項--whitelist或通過配置文件來完成

2.可以在PHPUnit配置信息中設置addUncoveredFilesFromWhitelist="true"來將白名單中包含的所有文件全部加入到代碼覆蓋率報告中

C.略過代碼塊

1.一些代碼是無法對其進行測試的,可以用@codeCoverageIgnore、@codeCoverageIgnoreStart與@codeCoverageIgnoreaEnd標註

2.標註將會計為已執行,並且不會在代碼覆蓋情況中被高亮標記

D.指明要覆蓋的方法

1.@covers標註可以用在測試代碼中來指明測試方法想要對哪些方法進行測試,如果提供了這個信息,則只有指定方法的代碼覆蓋率信息會被統計

2.可以用@coversNothing標註來指明一個測試不

BankAccountTest11_1.php

十二、測試的其他用途

A.敏捷文檔

1.極限編程要求群體代碼所有權(collective code ownership),因此所有開發者都需要知道整個系統是如何工作的

2.PHPUnit的TestDox功能著眼於測試類及其所有測試方法的名稱

3.敏捷文檔也可以以HTML或純文本格式生成,並寫入文件中,用--testdox-html和--testdox-text參數即可

B.跨團隊測試

1.一旦用測試將假設文檔化,你就擁有了測試

十三、Logging(日誌記錄)

1.PHPUnit所生成的測試結果XML日誌文件是基於JUnit task for Apache Ant所使用的XML日誌的

2.PHPUnit所生成的XML格式代碼覆蓋率信息日誌記錄不嚴格地基於Clover,所使用的XML日誌的

3.以易於常人瞭解(human-readable)的格式生成代碼覆蓋率,輸出到命令行或保存成文本文件

十四、擴展PHPUnit

1.編寫自定義斷言時,最佳實踐是遵循PHPUnit自有斷言的實現方式

https://github.com/zhangyue0503/php/tree/master/phpunit

https://phpunit.de/manual/current/zh_cn/phpunit-book.html


分享到:


相關文章: