單元測試最佳實踐:如何充分利用您的測試自動化?
什么是單元測試?
單元測試是測試應(yīng)用程序的各個單元或組件的實踐,以驗證每個單元是否正常工作。通常,unit(單元)應(yīng)該是應(yīng)用程序的一小部分 - 在Java中,它通常是單個類。請注意,并不是嚴(yán)格定義“unit”,開發(fā)人員需要決定每個測試的測試代碼范圍。
人們有時將“單元測試”一詞與“集成測試”或“端到端測試”進(jìn)行對比。不同之處在于,通常,單元測試是用于驗證單個可測試單元的行為,而集成測試則驗證多個組件的行為或整個應(yīng)用程序的行為。就像我所說,構(gòu)成“單元”的定義并沒有嚴(yán)格定義,由你來決定每個測試的范圍。
為什么選擇單元測試?
單元測試是一種經(jīng)過驗證的技術(shù),可確保軟件質(zhì)量,并帶來很多好處。以下是選擇單元測試的一些重要原因:
- 單元測試驗證了您的每個軟件不僅在今天可以正常運(yùn)行,而且在將來也繼續(xù)工作,為未來的發(fā)展奠定了堅實的基礎(chǔ)。
- 單元測試在生產(chǎn)過程的早期階段識別了缺陷,這減少了在開發(fā)周期的后期階段修復(fù)它們的成本。
- 經(jīng)過單元測試的代碼通常更容易重構(gòu),因為可以快速重新運(yùn)行測試以驗證行為沒有改變。
- 編寫單元測試迫使開發(fā)人員考慮生產(chǎn)代碼的設(shè)計如何使其適合于單元測試,并使開發(fā)人員從不同的角度看待他們的代碼,鼓勵他們在實現(xiàn)中考慮極端情況和錯誤條件。
- 在代碼審查過程中包括單元測試可以揭示修改后的代碼或新代碼應(yīng)該如何工作。此外,審核人員可以確認(rèn)測試是否良好。
不幸的是,經(jīng)常開發(fā)人員要么根本不編寫單元測試,要么沒有編寫足夠的測試,要么不維護(hù)它們??梢岳斫?,畢竟單元測試有時候?qū)懫饋砗芗郑蛘呔S護(hù)起來很費(fèi)時。有時會遇到最后期限,感覺編寫測試會讓我們錯過截止日期。但是,沒有編寫足夠的單元測試或沒有編寫好的單元測試容易跳入一個陷入風(fēng)險的陷阱。
因此,請考慮以下最佳實踐建議,了解如何編寫干凈,可維護(hù),自動化的測試,以最少的時間和精力為您提供單元測試的所有好處。
單元測試最佳實踐
讓我們看一下構(gòu)建,運(yùn)行和維護(hù)單元測試的一些最佳實踐,以獲得最佳結(jié)果。
單元測試應(yīng)該值得信賴
如果代碼被破壞,并且只有代碼被破壞,測試才會失敗。如果沒有,我們無法相信測試結(jié)果告訴我們的是什么。
單元測試應(yīng)該是可維護(hù)和可讀的
當(dāng)生產(chǎn)代碼發(fā)生變化時,通常需要更新測試,并且可能還需要調(diào)試。所以它必須易于閱讀和理解測試,不僅適用于編寫它的人,也適用于其他開發(fā)人員。始終組織和命名您的測試,以提高清晰度和可讀性。
單元測試應(yīng)驗證單個用例
好的測試驗證一件事或者只驗證一件事,這意味著他們通常會驗證一個用例。遵循此最佳實踐的測試更簡單,更易理解,這有利于可維護(hù)性和調(diào)試。驗證多個事物的測試很容易變得復(fù)雜且耗時。不要讓這種情況發(fā)生。
另一個最佳實踐是使用最少數(shù)量的斷言。有些人建議每次測試只有一個斷言(這可能有點過于嚴(yán)格限制了); 我們的想法是專注于僅驗證您正在測試的用例所需的內(nèi)容。
單元測試應(yīng)該是隔離的
測試應(yīng)該可以在任何機(jī)器上以任何順序運(yùn)行,而不會相互影響。如果可能,測試應(yīng)該不依賴于環(huán)境因素或全局/外部狀態(tài)。具有這些依賴性的測試更難以運(yùn)行并且通常不穩(wěn)定,使得它們更難以調(diào)試和修復(fù),并且最終花費(fèi)的時間比它們節(jié)省的時間更多(參見上面的可靠信息)。
幾年前Martin Fowler撰寫了一篇關(guān)于代碼的文章,描述了應(yīng)用程序代碼中的依賴用法,以及如何相應(yīng)地設(shè)計測試。在他的文章中,“孤獨(dú)”代碼不依賴于其他單元(它更獨(dú)立),而“社交”代碼確實與其他組件交互。如果應(yīng)用程序代碼是單獨(dú)的,那么測試很簡單......但是對于正在測試的社交代碼,您可以構(gòu)建“單獨(dú)”或“社交”測試?!吧缃粶y試”將依賴于實際依賴性以驗證行為,而“孤立測試”將測試中的代碼與依賴性隔離開來。您可以使用模擬來隔離測試中的代碼,并為“社交”構(gòu)建“單獨(dú)”測試 碼。我們將在下面看看如何做到這一點。
圖1:社交與孤獨(dú)測試。資料來源:Martin Fowler,2014年
一般來說,使用mock(模擬)作為依賴項使我們作為測試人員的生活更容易,因為我們可以為社會化代碼生成“單獨(dú)的測試”。對復(fù)雜代碼的社交測試可能需要大量設(shè)置,并且可能違反被隔離和可重復(fù)的原則。但是由于模擬是在測試中創(chuàng)建和配置的,因此它是自包含的,我們可以更好地控制依賴項的行為。另外,我們可以測試更多代碼路徑。例如,我可以返回自定義值或從模擬中拋出異常,以覆蓋邊界或錯誤條件。
單元測試應(yīng)該是自動化的
確保測試正在自動化過程中運(yùn)行。這可以是每天,也可以是每小時,也可以是持續(xù)集成或交付流程。報告需要可供團(tuán)隊中的每個人訪問和審核。作為一個團(tuán)隊,請討論您關(guān)注的指標(biāo):代碼覆蓋率,修改后的代碼覆蓋率,正在運(yùn)行的測試數(shù)量,性能等。
通過觀察這些數(shù)字可以學(xué)到很多東西,而這些數(shù)字的巨大變化往往表明可以立即解決的回歸問題。
使用良好的單元和集成測試混合
Michael Cohn的書“ 使用敏捷成功:使用Scrum進(jìn)行軟件開發(fā)”, 使用測試金字塔模型解決了這個問題(見下圖中的插圖)。這是描述測試資源理想分布的常用模型。這個想法是,當(dāng)你進(jìn)入金字塔時,測試通常構(gòu)建起來更復(fù)雜,更脆弱,運(yùn)行速度更慢,調(diào)試速度更慢。較低級別更加獨(dú)立,更集成,更快速,更易于構(gòu)建和調(diào)試。因此,自動化單元測試應(yīng)該構(gòu)成您的大部分測試。
單元測試應(yīng)驗證所有細(xì)節(jié),邊角情況和邊界條件等。應(yīng)更謹(jǐn)慎地使用組件,集成,UI和功能測試,以驗證API或應(yīng)用程序的整體行為。手動測試應(yīng)該是整個金字塔結(jié)構(gòu)的最小百分比,但仍然可用于發(fā)布驗收和探索性測試。該模型為組織提供了高水平的自動化和測試覆蓋率,因此他們可以擴(kuò)展測試工作并將與構(gòu)建,運(yùn)行和維護(hù)測試相關(guān)的成本保持在最低水平。
單元測試應(yīng)在有組織的測試實踐中執(zhí)行
為了在各個層面推動測試的成功,并使單元測試過程具有可擴(kuò)展性和可持續(xù)性,您將需要一些額外的實踐。首先,這意味著在編寫應(yīng)用程序代碼時編寫單元測試。一些組織在應(yīng)用程序代碼(測試驅(qū)動或行為驅(qū)動編程)之前編寫測試。重要的是測試與應(yīng)用程序代碼齊頭并進(jìn)。甚至應(yīng)該在代碼審查過程中一起審查測試和應(yīng)用程序代碼。評論可以幫助您理解正在編寫的代碼(因為他們可以看到預(yù)期的行為)并改進(jìn)測試!
編寫測試以及代碼不僅適用于新行為或計劃更改,它對于錯誤修復(fù)也很重要。您修復(fù)的每個錯誤都應(yīng)該有一個測試,以驗證錯誤是否已修復(fù)。這可以確保錯誤在將來保持不變。
對失敗的測試采用零容忍策略。如果您的團(tuán)隊忽略了測試結(jié)果,那么為什么要進(jìn)行測試呢?測試失敗應(yīng)該表明真正的問題......所以在他們浪費(fèi)QA的時間之前立即解決這些問題,或者更糟糕的是,他們進(jìn)入已發(fā)布的產(chǎn)品。
解決故障所需的時間越長,這些故障最終會花費(fèi)您的組織的時間和金錢就越多。因此,在重構(gòu)期間運(yùn)行測試,在提交代碼之前運(yùn)行測試,并且在測試通過之前不要將任務(wù)視為“完成”。
最后,保持這些測試。正如我之前所說,如果你在應(yīng)用程序發(fā)生變化時沒有及時更新這些測試,那么它們就會失去價值。特別是如果他們失敗了,那么失敗的測試每次失敗都需要花費(fèi)時間和金錢來進(jìn)行調(diào)查。當(dāng)代碼更改時,根據(jù)需要重構(gòu)測試。
正如您所看到的,最大化您在單元測試中投入的金錢和時間的回報需要在應(yīng)用最佳實踐方面進(jìn)行一些投資。但最終,最后的獎勵值得初步投資。
代碼覆蓋率如何?
通常,代碼覆蓋率是在自動化測試運(yùn)行時執(zhí)行多少生產(chǎn)代碼的度量。通過運(yùn)行一系列測試并查看代碼覆蓋率數(shù)據(jù),您可以大致了解正在測試的應(yīng)用程序的數(shù)量。
代碼覆蓋范圍很多 - 最常見的是線覆蓋和分支覆蓋。大多數(shù)工具都專注于線路覆蓋,它只是告訴您是否覆蓋了特定線路。分支更精細(xì),因為它告訴您是否覆蓋了代碼中的每條路徑。
代碼覆蓋率是一個重要的指標(biāo),但請記住,增加它是達(dá)到目的的手段。它非常適合在測試中找到差距,但這并不是唯一需要關(guān)注的問題。注意不要花費(fèi)太多精力來實現(xiàn)100%的覆蓋率 - 它甚至可能不可行或不可行,而且測試的質(zhì)量確實是重要的。話雖如此,為您的項目實現(xiàn)至少60%的覆蓋率是一個很好的起點,80%或更多是一個很好的目標(biāo)。顯然,由您來決定該目標(biāo)應(yīng)該是什么。
如果您擁有自動化工具,不僅可以測量代碼覆蓋率,還可以跟蹤測試覆蓋的修改代碼量,這也很有價值,因為這可以提供對是否正在編寫足夠的測試以及生產(chǎn)代碼更改的可見性。
請參閱Parasoft報告和分析中心的示例代碼覆蓋率報告,如果您使用Parasoft Jtest進(jìn)行單元測試,則可以瀏覽:
另外要記住的是,在編寫新測試時,要注意單獨(dú)關(guān)注行覆蓋,因為單行代碼可能導(dǎo)致多個代碼路徑,因此請確保測試驗證這些代碼路徑。線路覆蓋是一個有用的快速指示器,但它不是唯一要尋找的東西。
增加覆蓋率的最明顯方法是為更多代碼路徑添加更多測試,以及測試方法的更多用例。增加覆蓋率的有效方法是使用參數(shù)化測試。對于Junit4,內(nèi)置了Junit4參數(shù)化功能和第三方庫,如JunitParams。Junit5具有內(nèi)置參數(shù)化功能。
最后,如果您還沒有跟蹤測試覆蓋率,我強(qiáng)烈建議您現(xiàn)在就開始。有很多工具可以提供幫助,比如Parasoft Jtest。首先測量您當(dāng)前的覆蓋率數(shù)字,然后設(shè)定應(yīng)該在哪里的目標(biāo),首先解決重要的差距,然后從那里開始工作。
總結(jié)
盡管單元測試是一種用于確保軟件質(zhì)量的成熟技術(shù),但它仍然被認(rèn)為是開發(fā)人員的負(fù)擔(dān),許多團(tuán)隊仍然在努力解決這個問題。為了充分利用測試和自動化測試工具,測試必須值得信賴,可維護(hù),可讀,獨(dú)立,并用于驗證單個用例。自動化是使單元測試可行且可擴(kuò)展的關(guān)鍵。
此外,軟件團(tuán)隊需要練習(xí)良好的測試技術(shù),例如編寫和審查測試以及應(yīng)用程序代碼,維護(hù)測試以及確保立即跟蹤和修復(fù)失敗的測試。采用這些單元測試最佳實踐可以快速提高單元測試結(jié)果。
想要了解Parasoft、Parasoft SOAtest、Parasoft Virtualize更多信息或資源的朋友,請點擊這里~