都100%程式碼覆蓋了,還會有什麼問題?
引言
很多人看到這個標題時,都會想“你都100%程式碼覆蓋了,怎麼還會有問題呢?” 讓我們看一下程式碼例子:
1234567 | publicclassTestCalculator{publicDoubleadd(Doublea,Double |
再看看用junit寫出的測試程式碼:
123456789101112 | @TestpublicvoidtestAdd(){Doublea=newDouble(1);Doubleb=newDouble(2);Doublec=newDouble(3);assertEquals(c,testCalculator.add(a,b));} |
當我們使用EclEmma或者Jacoco來進行覆蓋測試時,對於這個類,我們將得到100%測試覆蓋率。
一切看起來都那麼的完美,真是這樣的嗎?
好吧,讓我們來來看看另一個測試,當其中一個變數為null時,返回值將會是什麼?
123456789101112 | @TestpublicvoidtestAddNullPointerException(){Doublea=newDouble(1);Doubleb=null;Doublec=newDouble(3);assertEquals(c,testCalculator.add(a,b));} |
好了,你會發現儘管覆蓋率為100%,但程式卻丟擲了NullPointerException。
那麼肯定有人會問,這樣的話單元測試覆蓋率的高低都不能作為衡量專案程式碼質量的指標,那麼我們要單元測試還有什麼用?
首先,我想我們可能搞錯了測試覆蓋的定義。
我們先聽聽Martin Fowler對於測試覆蓋的定義:
Test coverage is a useful tool for finding untested parts of a codebase. Test coverage is of little use as a numeric statement of how good your tests are.
他認為:把測試覆蓋作為質量目標沒有任何意義,我們應該把它作為一種發現未被測試覆蓋的程式碼的手段。
所以100%的程式碼覆蓋率還值得追求嗎?
當然,這應該是每個程式設計師畢生的追求之一,但是如果從專案角度考慮ROI(投入產出比),對於需要快速上線的短期專案,需要注重的是讓測試覆蓋核心功能程式碼。如果你的專案是一個長期專案,那麼高覆蓋率是非常有必要的,它意味著高可維護性,以及更少的bug。(前提是你的測試採用TDD/BDD方式編寫,我見過將測試程式碼寫的一團糟的人,看著他的程式碼,我寧願重新寫一遍)
那麼對於一個專案來說,覆蓋率應該達到多少?
其實沒有適用於所有專案的數值,每個專案都應有自己的閾值,但共性是,測試必須覆蓋主要業務場景,程式碼的邏輯分支也必須儘可能的覆蓋。
如何改進你的專案程式碼覆蓋率?
首先我們要閱讀和理解專案程式碼,找出其中需要測試並且與業務強相關的程式碼,結合sonar等程式碼質量管理平臺,從程式碼編寫規範、複雜度、重複程式碼等方面進行程式碼重構,進一步提高專案的可維護性與可讀性。
這也意味著重構,重構的同時,你需要更多的測試來保證你重構程式碼的正確性。
其次要對code coverage進行度量分析,那麼我們應該怎麼度量code coverage?
一般來說我們從以下四個維度來度量,如上圖所示:
- 行覆蓋率(line coverage):度量被測程式碼中每個可執行語句是否都被執行到,但不包括java import,空行,註釋等。
- 函式覆蓋率(function coverage):度量被測程式碼中每個定義的函式是否都被呼叫。
- 分支覆蓋率(branch coverage):度量被測程式碼中每一個判定的分支是否都被測試到。
- 語句覆蓋率(statement coverage):度量被測程式碼是否每個語句都被執行。
所以行覆蓋率的高低不能說明專案的好壞,我們要從多方面進行思考,一般我們遵循的標準應是:函式覆蓋率 > 分支覆蓋率 > 語句覆蓋率**。
程式碼覆蓋率最重要的意義在於:
- 閱讀分析之前專案中未覆蓋部分的程式碼,進而反推在前期QA以及相關測試人員在進行黑盒測試設計時是否考慮充分,沒有覆蓋到的程式碼是否是測試設計的盲點,為什麼沒有考慮到?是需求或者UX設計不夠清晰,還是測試設計的理解有誤。
- 檢測出程式中的廢程式碼,可以逆向反推程式碼設計中不合理的地方,提醒設計/開發人員理清程式碼邏輯關係,提升程式碼質量。
- 程式碼覆蓋率高不能說明程式碼質量高,但是反過來看,程式碼覆蓋率低,程式碼質量絕對不會高到哪裡去,可以作為測試自我審視的重要工具之一。
結束語
單元測試的覆蓋率並不只是為了取悅客戶或者管理層的資料,它能夠實實在在反應專案中程式碼的健康程度,幫助我們更好的改善了程式碼的質量,增加了我們對所編寫程式碼的信心。