簡體   English   中英

PHPUnit 組織測試的最佳實踐

[英]PHPUnit best practices to organize tests

我目前打算從頭開始對一個項目進行 phpunit 測試。 所以我正在研究一些項目(比如 Zend),看看他們是如何做事的,以及他們如何組織他們的測試。

大多數事情都非常清楚,我唯一遇到的問題是如何正確組織測試套件。 Zend 有一個 AllTests.php,從中加載其他測試套件。
仔細查看它使用PHPUnit_Framework_TestSuite創建套件對象然后向其中添加其他套件的類,但是如果我查看 PHPUnit 文檔以在 3.4 之后的 PHPUnit 版本中組織測試,則只有 XML 或 FileHierarchy 的描述。 使用類來組織測試的那個被刪除了。
我還沒有發現這種方法已被棄用,而且 Zend 等項目仍在使用它。

但是,如果它被棄用,我如何能夠使用 xml 配置以相同的結構組織測試? 執行所有測試沒問題,但是如果我只想執行幾個測試,我將如何組織測試(在 xml 中)。 也許創建幾個 xml,我只指定要運行的幾個測試/測試套件?

因此,如果我只想測試應用程序的模塊 1 和模塊 2,我是否會為每個模塊提供一個額外的 xml,並僅為其中的那些模塊(模塊使用的類)定義測試套件。 還有一個為所有測試定義一個測試套件?

或者在特定測試上使用@group注釋來標記它們是用於 module1 還是 module2 會更好嗎?

預先感謝您向我指出一些最佳實踐。

我將首先鏈接到手冊,然后進入我在該領域所見所聞。

組織 phpunit 測試套件

文件系統中的模塊/測試文件夾組織

我推薦的方法是將文件系統與 xml 配置相結合。

tests/
 \ unit/
 | - module1
 | - module2
 - integration/
 - functional/

帶有一個簡單的phpunit.xml

<testsuites>
  <testsuite name="My whole project">
    <directory>tests</directory>
  </testsuite>
</testsuites>

如果您願意,您可以拆分測試套件,但這是一個項目選擇。

運行phpunit將執行所有測試,運行phpunit tests/unit/module1 phpunit將運行phpunit tests/unit/module1所有測試。

“單元”文件夾的組織

這里最常見的方法是在你的tests/unit/文件夾結構中鏡像你的source/目錄結構。

無論如何,每個 ProductionClass 都有一個 TestClass,所以在我的書中這是一個很好的方法。

在文件組織中

  • 每個文件一個類。

如果一個文件中有多個測試類,它無論如何都不會起作用,因此要避免這種陷阱。

  • 沒有測試命名空間

它只是使編寫測試變得更加冗長,因為您需要一個額外的 use 語句,所以我會說 testClass 應該與生產類位於相同的命名空間中,但這不是 PHPUnit 強迫您做的。 我剛剛發現它更容易,沒有缺點。

只執行幾個測試

例如phpunit --filter Factory執行所有 FactoryTests 而phpunit tests/unit/logger/執行所有與日志相關的內容。

您可以將@group標簽用於諸如問題編號、故事之類的內容,但對於“模塊”,我會使用文件夾布局。

多個xml文件

如果您想擁有以下內容,創建多個 xml 文件會很有用:

  • 一個沒有代碼覆蓋
  • 一個僅用於單元測試(但不適用於功能或集成或長時間運行的測試)
  • 其他常見的“過濾器”案例
  • 例如 PHPBB3 為their phpunit.xmls做這their phpunit.xmls

測試的代碼覆蓋率

因為它與使用測試啟動新項目有關:

  • 我的建議是使用我博客中描述的@covers標簽(僅用於單元測試,始終覆蓋所有非公共功能,始終使用covers 標簽。
  • 不要為您的集成測試生成覆蓋率。 它會給你一種虛假的安全感。
  • 始終使用白名單來包含您的所有生產代碼,這樣數字就不會騙您!

自動加載和引導您的測試

您的測試不需要任何類型的自動加載。 PHPUnit 會處理這個問題。

使用<phpunit bootstrap="file">屬性來指定您的測試引導程序。 tests/bootstrap.php是一個放置它的好地方。 在那里你可以設置你的應用程序自動加載器等等(或者為此調用你的應用程序引導程序)。

概括

  • 幾乎所有內容都使用 xml 配置
  • 分離單元測試和集成測試
  • 您的單元測試文件夾應該反映您的應用程序文件夾結構
  • 要僅執行phpunit --filter測試,請使用phpunit --filterphpunit tests/unit/module1
  • 從一開始就使用strict模式,永遠不要關閉它。

要查看的示例項目

基本目錄結構

我一直在嘗試將測試代碼放在被測試的代碼旁邊,實際上是在同一個目錄中,文件名與正在測試的代碼文件略有不同。 到目前為止,我喜歡這種方法。 這個想法是您不必花費時間和精力來保持代碼和測試代碼之間的目錄結構同步。 因此,如果您更改代碼所在目錄的名稱,那么您也不需要去查找並更改測試代碼的目錄名稱。 這也使您可以花更少的時間尋找與某些代碼配套的測試代碼,因為它就在它旁邊。 這甚至可以減少使用測試代碼創建文件的麻煩,因為您不必首先找到包含測試的目錄,可能會創建一個新目錄以匹配您為其創建測試的目錄,並且然后創建測試文件。 您只需在那里創建測試文件。

這樣做的一個巨大優勢是它意味着其他員工(不是你,因為你永遠不會這樣做)將不太可能避免編寫測試代碼,因為它只是太多的工作。 即使他們向現有類添加方法,他們也不太可能不想向現有測試代碼添加測試,因為查找測試代碼的摩擦很小。

一個缺點是這使得在沒有伴隨測試的情況下發布生產代碼變得更加困難。 盡管如果您使用嚴格的命名約定,它仍然是可能的。 例如,我一直在使用 ClassName.php、ClassNameUnitTest.php 和 ClassNameIntegrationTest.php。 當我想運行所有單元測試時,有一個套件可以查找以 UnitTest.php 結尾的文件。 集成測試套件的工作方式類似。 如果我願意,我可以使用類似的技術來防止測試發布到生產中。

這種方法的另一個缺點是當您只是在尋找實際代碼而不是測試代碼時,需要花費更多的精力來區分兩者。 但我覺得這實際上是一件好事,因為它迫使我們感受到測試代碼也是代碼這一現實的痛苦,它增加了自己的維護成本,並且與其他任何東西一樣是代碼的重要組成部分,而不是只是某個地方的東西。

每節課一個測試課:

這對大多數程序員來說遠非實驗性的,但對我來說卻是。 我正在試驗每個被測試的課程只有一個測試課程。 過去,我為每個被測試的類都有一個完整的目錄,然后在該目錄中有幾個類。 每個測試類都以某種方式設置被測試的類,然后有一堆方法,每個方法都有不同的斷言。 但后來我開始注意到我將這些對象放入的某些條件與它從其他測試類進入的其他條件有共同之處。 重復變得難以處理,所以我開始創建抽象來刪除它。 測試代碼變得非常難以理解和維護。 我意識到了這一點,但我找不到對我有意義的替代方案。 每個類只有一個測試類似乎無法測試幾乎足夠的情況,而不會在一個測試類中包含所有測試代碼而變得不堪重負。 現在我對此有了不同的看法。 即使我是對的,這也是其他程序員和我自己想要編寫和維護測試的巨大障礙。 現在我正在嘗試強迫自己為每個正在測試的課程設置一個測試課程。 如果我在一個測試類中遇到太多要測試的東西,我會嘗試將此視為正在測試的類做得太多的跡象,應該分成多個類。 為了消除重復,我試圖盡可能地堅持更簡單的抽象,以允許所有內容都存在於一個可讀的測試類中。

更新我仍在使用並喜歡這種方法,但我發現了一種非常好的技術來減少測試代碼的數量和重復的數量。 在測試類本身中編寫可重用的斷言方法很重要,該方法被該類中的測試方法大量使用。 如果我將它們視為內部 DSL(鮑勃叔叔提倡的東西,實際上他提倡實際制作內部 DSL),這有助於我提出正確類型的斷言方法。 有時您可以通過接受一個字符串參數來進一步理解這個 DSL 概念(實際上是創建一個 DSL),該參數具有一個簡單的值,該值指代您嘗試執行的測試類型。 例如,有一次我創建了一個可重用的斷言方法,它接受一個 $left、$comparesAs 和一個 $right 參數。 這使得測試變得非常簡短和可讀,因為代碼讀取了類似$this->assertCmp('a', '<', 'b')

老實說,我怎么強調這一點都不為過,它是使編寫測試具有可持續性的整個基礎(您和其他程序員希望繼續這樣做)。 這使得測試增加的價值可能超過它們帶走的價值。 關鍵不是你需要使用那種精確的技術,關鍵是你需要使用某種可重用的抽象,允許你編寫簡短易讀的測試。 看起來我似乎脫離了這個問題,但我真的不是。 如果你不這樣做,你最終會陷入需要為每個被測試的類創建多個測試類的陷阱,事情真的從那里開始崩潰。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM