編寫高品質的測試
不幸的是,測試在許多組織中仍然沒有得到應有的關注。有時,如果開發人員沒有編寫任何測試,他們會感到內疚,同時測試程式碼往往沒有得到適當的審查。相反,評論中經常檢查的唯一事情是是否有任何測試,這是一種恥辱,因為僅僅進行測試還不夠好。實際上,它們至少應該與專案中的所有其他程式碼具有相同的質量,即使不是更高的品質。否則,測試確實可能會阻礙您,因為測試失敗的次數太多,難以理解,或者運行時間太長。我已經在關於使用記憶體中實作而不是儲存庫模擬的部落格文章中討論了其中的一些要點。現在我想討論一些其他的、更一般的、我在寫測試時要注意的事情。
極簡主義是關鍵
Stack Overflow 要求您為問題添加最少的、可重現的範例,在我看來,這對於出於完全相同的原因編寫測試也是非常好的建議。尤其是在編寫測試幾個月後閱讀測驗時,如果發生的事情較少,就更容易完全理解正在發生的事情。因此只編寫測試絕對必要的程式碼,並抵制僅僅因為這樣做很容易就添加更多內容的誘惑。但測試程式碼當然仍然必須完整,即測試應包含盡可能多的行,但盡可能少。
追求 100% 的程式碼覆蓋率
這可能是一個不受歡迎的觀點,但我認為以 100% 程式碼覆蓋率為目標是完全有意義的,儘管許多人似乎認為這是一種不好的做法。
有時團隊會選擇較低的值,例如程式碼覆蓋率達到 90%。然而,這對我來說沒有多大意義。首先,所有這些數字都有些隨意,很難使用資料進行備份。此外,在編寫新程式碼時,並非所有程式碼都需要經過測試才能通過該閾值。如果有人設法提高了覆蓋率,那麼下一個人可能根本不編寫任何測試,同時仍然保持高於 90% 的程式碼覆蓋率,這會導致錯誤的自信感。
我經常聽到的藉口之一是為 getter 和 setter 等簡單函數編寫測試沒有意義。也許令人驚訝的是,我完全同意這一點。但這裡有一個問題:如果沒有一個測試真正使用這些 getter 和 setter,那麼可能就沒有必要使用它們。 因此,與其抱怨實現 100% 測試覆蓋率有多麼困難,最好不要先編寫不需要的程式碼。這也避免了每行程式碼帶來的維護負擔。
但是,有一個小問題:有時程式碼會執行奇怪的操作,這可能會導致程式碼覆蓋工具將某些行標記為未覆蓋,即使它是在測試運行期間執行的。我沒有經常遇到這樣的情況,但如果沒有辦法使這項工作正常進行,我會將它們排除在程式碼覆蓋範圍之外。例如。 PHPUnit 允許使用他們的 codeCoverageIgnore 註解來做到這一點:
<?php class SomeClass { /** * @codeCoverageIgnore */ public function doSomethingNotDetectedAsCovered() { } }
這樣這個函數就不會被包含在程式碼覆蓋率分析中,這意味著仍然有可能達到 100% 的程式碼覆蓋率,而且我也會不斷檢查該值。另一種方法是選擇低於 100% 的值,但這樣會出現上面提到的相同問題:其他程式碼也可能不會被測試覆蓋,並且可能會被遺漏。
話雖如此,100% 的程式碼覆蓋率當然不能保證您的程式碼沒有任何錯誤。但是,如果您的應用程式程式碼中確實有未覆蓋的行,您甚至不會對測試進行更改以發現該行中的潛在錯誤。
寫出好的斷言
寫測試的原因是我們想要斷言程式碼的某種行為。因此斷言是測試中非常重要的一部分。
當然,編寫斷言時最重要的考慮因素是它正確地測試程式碼的行為。但緊隨其後的是程式碼失敗時斷言的行為方式。如果斷言由於某種原因失敗,那麼問題對於開發人員來說應該盡可能明顯。在此 Symfony 拉取請求中目前正在處理的情況就是這種情況顯而易見的情況。 Symfony 附帶了一個assertResponseStatusCodeSame 方法,它允許在功能測試中檢查回應的狀態碼:
<?php declare(strict_types=1); class LoginControllerTest extends WebTestCase { public function testFormAttributes(): void { $client = static::createClient(); $client->request('GET', '/login'); $this->assertResponseStatusCodeSame(200); $this->assertSelectorCount(1, 'input[name="email"][required]'); } }
這個測試的問題是它在狀態碼不是 200 的情況下產生的輸出。由於測試通常在開發環境中運行,因此當訪問這個 URL 時 Symfony 將傳回一個錯誤頁面,並且assertResponseStatusCodeSame 方法將輸出斷言失敗時的完整回應。這個輸出非常長,因為它不僅返回 HTML,還返回 CSS 和 JavaScript,而且我的回滾緩衝區實際上太小,無法讓我閱讀整個訊息。
這絕對是我迄今為止遇到的最糟糕的例子,但如果程式碼中使用了錯誤的斷言,它也會很煩人。讓我們來看看上面的assertSelectorCount斷言的輸出,如果給定的選擇器沒有恰好產生一個元素,則該斷言會失敗並顯示以下訊息:
Failed asserting that the Crawler selector "input[name="email"][required]" was expected to be found 1 time(s) but was found 0 time(s).
它很好地了解了發生的問題。但是,斷言也可以用不同的方式編寫(不要在家裡這樣做!):
<?php class SomeClass { /** * @codeCoverageIgnore */ public function doSomethingNotDetectedAsCovered() { } }
有人可能會說這完全一樣,因此使用哪種變體並不重要。這與事實相差甚遠,因為如果電子郵件沒有單一必填輸入字段,則會出現以下訊息:
<?php declare(strict_types=1); class LoginControllerTest extends WebTestCase { public function testFormAttributes(): void { $client = static::createClient(); $client->request('GET', '/login'); $this->assertResponseStatusCodeSame(200); $this->assertSelectorCount(1, 'input[name="email"][required]'); } }
這根本沒有幫助,無論誰致力於解決問題,首先都必須弄清楚問題到底是什麼。這表明,始終應該使用合適的斷言,並且 PHPUnit 附帶了許多適合所有類型用例的斷言。有時創建自訂斷言甚至是有意義的。
近年來我看到越來越流行的一個相對較新的斷言是快照測試。尤其是當開始從事前端專案時,它似乎有很大幫助。我過去經常將它與 React 一起使用。主要要點是您的測試看起來像這樣:
Failed asserting that the Crawler selector "input[name="email"][required]" was expected to be found 1 time(s) but was found 0 time(s).
神奇的事情發生在 toMatchSnapshot 方法中。在第一次運行時,它將樹變數的內容寫入單獨的檔案中。在後續運行中,它將樹值的新值與先前儲存在單獨檔案中的值進行比較。如果某些內容發生更改,它將導致測試失敗並顯示差異,並可以選擇再次更新快照,這意味著您可以立即修復測試。
雖然這聽起來確實不錯,但它也有一些缺點。首先,快照非常脆弱,因為每當元件的渲染標記發生變更時,測試就會失敗。其次,測試的意圖是隱藏的,因為它沒有解釋作者真正想要測試的內容。
但是,我真正喜歡它的是,每當我更改組件時,它都會提醒我使用該組件的所有其他組件,因為所有這些快照在下次運行時都會失敗。出於這個原因,我喜歡每個組件至少進行一次快照測試。
結論
總而言之,我認為您可以立即開始做一些事情來提高測試品質:
- 將測試中的程式碼保持在絕對需要的最低限度
- 目標是 100% 的程式碼覆蓋率,如果無法測試,請正確地將程式碼從程式碼覆蓋率機制中排除
- 當測試失敗時,使用正確的斷言以獲得更好的錯誤訊息
在我看來,遵循這幾條規則已經會產生巨大的影響,並幫助您長時間享受在程式碼庫中工作!
以上是編寫高品質的測試的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

在PHP中,應使用password_hash和password_verify函數實現安全的密碼哈希處理,不應使用MD5或SHA1。1)password_hash生成包含鹽值的哈希,增強安全性。 2)password_verify驗證密碼,通過比較哈希值確保安全。 3)MD5和SHA1易受攻擊且缺乏鹽值,不適合現代密碼安全。

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。

PHP類型提示提升代碼質量和可讀性。 1)標量類型提示:自PHP7.0起,允許在函數參數中指定基本數據類型,如int、float等。 2)返回類型提示:確保函數返回值類型的一致性。 3)聯合類型提示:自PHP8.0起,允許在函數參數或返回值中指定多個類型。 4)可空類型提示:允許包含null值,處理可能返回空值的函數。

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。
