對于單元測試這個方法,行業人士都應該不會陌生。它是工作中常見的測試方法,就目前它還有很多改進的空間,事實證明最有效的單元測試最佳實踐,這也包括一些最大化自動化工具的方法。在日常工作中,軟件團隊需要不斷練習測試技術,維護測試和確保立即跟蹤,還要糾正失敗的測試,并且單元測試應在有組織的測試實踐中執行接下來我們就詳細介紹一單元測試的概念以及我們為什么要進行單元測試的問題。
什么是單元測試?
單元測試是測試應用程序的單個單元或組件的一種做法,目的是驗證每個單元或組件是否正常工作。通常,一個單元應該只占應用程序的一小部分——在Java中,它通常是單個類。請注意,我并不是在這里嚴格定義“單元”,而是由開發人員來決定每個測試的測試代碼范圍。
人們有時將“單元測試”與“集成測試”或“端到端測試”相對比。區別在于,通常通過進行單元測試來驗證單個可測試單元的行為,而集成測試則是在一起驗證多個組件或整個應用程序的行為。就像我說過的那樣,對“單元”的定義并沒有嚴格定義,具體取決于每個測試的范圍。
為什么要進行單元測試?
單元測試是一種行之有效的技術,可確保軟件質量,并帶來很多好處。這是(多個)進行單元測試的重要原因:
1、單元測試可以驗證您的每款軟件不僅可以在今天正常運行,而且可以在將來繼續運行,為將來的開發奠定了堅實的基礎。
2、單元測試可以在生產過程的早期階段識別出缺陷,從而降低了在開發周期的后期階段修復缺陷的成本。
3、單元測試的代碼通常更安全地重構,因為可以快速重新運行測試以驗證行為沒有改變。
4、編寫單元測試迫使開發人員考慮設計生產代碼以使其適合于單元測試的程度,并使開發人員從不同的角度看待他們的代碼,鼓勵他們在實現過程中考慮極端情況和錯誤情況。
5在代碼審查過程中包含單元測試可以揭示修改后的代碼或新代碼應如何工作。另外,審閱者可以確認測試是否良好。
不幸的是,過于頻繁的開發人員要么根本不編寫單元測試,要么沒有編寫足夠的測試,要么不維護它們。我了解——單元測試有時編寫起來很棘手,或者維護起來很耗時。有時會有一個截止日期,感覺就像編寫測試會讓我們錯過那個截止日期。但是沒有編寫足夠的單元測試或沒有編寫好的單元測試是一個容易陷入的陷阱。
因此,請考慮以下有關如何編寫干凈、可維護的自動化測試的最佳實踐建議,這些建議可以用最少的時間和精力為您提供單元測試的所有好處。
單元測試最佳實踐
讓我們看一些構建,運行和維護單元測試以達到最佳結果的最佳實踐。
· 單元測試應該值得信賴
如果代碼損壞并且只有代碼損壞,則測試必須失敗。否則,我們將無法相信測試結果在告訴我們什么。
· 單元測試應可維護且可讀
當生產代碼更改時,通常需要更新測試,也可能需要調試。因此,不僅對于編寫它的人,而且對于其他開發人員,都必須易于閱讀和理解該測試。為了清楚和易讀,請始終組織和命名測試。
· 單元測試應驗證單個用例
好的測試只能驗證一件事,而只能驗證一件事,這意味著通常情況下,它們只能驗證一個用例。遵循此最佳實踐的測試更簡單,更易理解,這對于可維護性和調試很有好處。驗證不止一件事的測試很容易變得復雜且維護耗時。不要讓這種情況發生。
另一個最佳實踐是使用最少數量的斷言。有人建議每個測試只聲明一個(可能有點太嚴格了)。這個想法是集中于僅驗證所測試用例所需的內容。
· 單元測試應隔離
測試應該可以在任何機器上以任何順序運行,而不會互相影響。如果可能,測試應不依賴于環境因素或全局/外部狀態。具有這些依賴項的測試較難運行,并且通常不穩定,從而使其更難以調試和修復,最終花費的時間超過了所節省的時間(請參見上面的可信賴信息)。
幾年前,馬丁·福勒(Martin Fowler)撰寫了有關“單獨的”與“可社交的”代碼的文章,以描述應用程序代碼中的依賴關系用法,以及如何相應地設計測試。在他的文章中,“單獨的”代碼不依賴于其他單元(它更加獨立),而“可聯系的”代碼確實與其他組件交互。如果應用程序代碼是單獨的,則測試很簡單,但是對于正在測試的社交代碼,您可以構建“單獨”或“社交”測試。“社交測試”將依賴于真實的依賴關系以驗證行為,而“單獨測試”則將受測代碼與依賴關系隔離開。您可以使用模擬來隔離被測代碼,并為“可社交”代碼構建“單獨”測試。我們將在下面查看如何執行此操作。
通常,使用模擬作為依賴項會使我們的測試人員生活更加輕松,因為我們可以為社交代碼生成“單獨的測試”。復雜代碼的社交測試可能需要大量設置,并且可能違反隔離和可重復的原則。但是,由于模擬是在測試中創建和配置的,因此它是獨立的,我們可以更好地控制依賴項的行為。另外,我們可以測試更多的代碼路徑。例如,我可以返回自定義值或從模擬中引發異常,以涵蓋邊界或錯誤情況。
單元測試應自動化
確保在自動化過程中運行測試。這可以是每天、每小時或在持續集成或交付過程中。團隊中的每個人都需要訪問并查看報告。作為一個團隊,討論您關心的指標:代碼覆蓋率、修改后的代碼覆蓋率、正在運行的測試數量、性能等。
通過查看這些數字可以學到很多東西,這些數字的巨大變化通常表明可以立即解決回歸問題。
結合使用單元測試和集成測試
邁克爾·科恩(Michael Cohn)的書《成功實現敏捷:使用Scrum進行軟件開發》使用測試金字塔模型解決了這一問題。這個想法是,隨著您進入金字塔,測試通常會更復雜、更脆弱、運行更慢、調試更慢。較低的級別更加隔離和集成、更快、更易于構建和調試。因此,自動化的單元測試應占您測試的大部分。
單元測試應驗證所有細節、極端情況和邊界條件等。應更謹慎地使用組件、集成、UI和功能測試,以驗證API或應用程序的整體行為。手動測試應該在整個金字塔結構中所占的比例最小,但對于發布驗收和探索性測試仍然有用。該模型為組織提供了高度的自動化和測試覆蓋范圍,因此他們可以擴大測試工作量,并將與構建、運行和維護測試相關的成本降至最低。
單元測試應在有組織的測試實踐中執行
為了在各個級別上推動測試的成功,并使單元測試過程具有可擴展性和可持續性,您將需要一些其他實踐。首先,這意味著在編寫應用程序代碼時編寫單元測試。一些組織在應用程序代碼之前編寫測試(測試驅動或行為驅動的編程)。重要的是測試與應用程序代碼緊密結合。測試和應用程序代碼甚至應該在代碼審查過程中一起審查。評論有助于您理解所編寫的代碼(因為他們可以看到預期的行為)并可以改善測試!
與代碼一起編寫測試不僅是針對新行為或計劃中的更改,對于修復錯誤也至關重要。您修復的每個錯誤均應進行測試,以驗證該錯誤是否已修復。這樣可以確保該錯誤在將來保持不變。
對測試失敗采取零容忍策略。如果您的團隊忽略測試結果,那為什么還要進行測試呢?測試失敗應該表明是真正的問題。因此,在浪費質量檢查人員的時間之前或更早就解決這些問題,或更糟糕的是,它們會進入發布的產品。
解決故障所需的時間越長,這些故障最終將花費您的組織更多的時間和金錢。因此,在重構期間運行測試,請在提交代碼之前立即運行測試,并且在測試通過之前也不要將任務視為“完成”。
最后,維護那些測試。正如我之前說過的,如果您在應用程序更改時沒有使這些測試保持最新狀態,則它們會失去價值。尤其是如果它們失敗了,則失敗的測試會浪費時間和金錢進行每次失敗的調查。當代碼更改時,根據需要重構測試。
如您所見,要使單元測試中的金錢和時間回報最大化,就需要在應用最佳實踐方面進行一些投資。但最終,這些回報值得進行初始投資。
那代碼覆蓋率呢?
通常,代碼覆蓋率是對自動化測試運行期間執行了多少生產代碼的度量。通過運行一組測試并查看代碼覆蓋率數據,您可以大致了解正在測試的應用程序數量。
代碼覆蓋范圍很多,最常見的是行覆蓋范圍和分支覆蓋范圍。大多數工具專注于行覆蓋率,它僅告訴您是否覆蓋特定行。分支更加精細,因為它告訴您是否覆蓋了代碼的每個路徑。
代碼覆蓋率是一項重要指標,但是請記住,增加覆蓋率是達到目的的一種手段。這對于發現測試中的差距非常有用,但這并不是唯一要關注的事情。注意不要花費太多的精力來嘗試達到100%的覆蓋率——這甚至可能是不可能或不可行的,實際上,測試的質量是很重要的。話雖如此,為您的項目至少達到60%的覆蓋率是一個不錯的起點,而設定80%或更高的覆蓋率是一個好的目標。顯然,由您決定目標是什么。
如果您擁有自動化的工具,這不僅很有價值,它不僅可以測量代碼覆蓋率,還可以跟蹤測試覆蓋了多少修改后的代碼,因為這可以使您了解是否編寫了足夠的測試以及生產代碼的更改。
要記住的另一件事是,在編寫新測試時,請注意不要只關注行覆蓋范圍,因為單行代碼可能會導致多個代碼路徑,因此請確保您的測試驗證這些代碼路徑。線覆蓋率是一個有用的快速指示器,但這并不是唯一要尋找的東西。
增加覆蓋率的最明顯方法就是簡單地為更多的代碼路徑添加更多的測試,以及被測方法的更多用例。增加覆蓋范圍的有效方法是使用參數化測試。對于Junit4.有內置的Junit4參數化功能和諸如JunitParams之類的第三方庫。Junit5具有內置的參數化功能。
最后,如果您尚未跟蹤測試范圍,強烈建議您開始。有很多工具可以提供幫助,例如Parasoft Jtest。首先測量您當前的覆蓋范圍數字,然后為應該覆蓋的范圍設定目標,首先解決重要的差距,然后再從那里開始工作。
以上我們分享了關于單元測試的相關內容。盡管單元測試是確保軟件質量的可靠技術,但現在還是很多開發人員的負擔,如果您想了解更多相關信息,請您繼續關注中培偉業。