你真的會寫單測嗎?TDD初體驗
前言:
昨天讀到了一篇文章,講的是TDD,即Test-Driven Development,測試驅動開發。大體意思是,它要求在編寫某個功能的程式碼之前先編寫測試程式碼,然後只編寫使測試通過的功能程式碼,通過測試來推動整個開發的進行。這有助於編寫簡潔可用和高質量的程式碼,並加速開發過程。
初讀之時,瞬間感受到了震撼,感覺和自己之前的開發流程全都不一樣,之前是由始至終,而這種思想確實以終為始。後來一查這種思想早在前幾年甚至前幾十年就被提出了,進而被廣泛運用到了敏捷開發中。看來是自己孤落寡聞了,於是我準備將這種思想用到今後的開發中,要做的第一件事,就是溫習如何寫用例。
為什麼是溫習?
早在實習的時候,我們研發組就有寫用例的習慣,但是隨著開發逐漸熟悉,這種習慣不知不覺就被丟棄了,有頁面的點點點,沒頁面的看邏輯。相信有很多人也像我一樣,不知不覺就把這項技能丟棄了,接下來就讓我們一起,去重新撿起這項技能。
工具選擇
Junit
對於一個Java開發工程師來說,一提到寫單測,我們最先想到的,一定是Junit。下面是maven座標
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
用Junit我們可以快速的,簡潔的用註解進行單元測試。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:conf/core/*.xml") public class ObjTest { @Test public void testFunc(){ //todo test } }
這裡要注意的是@ContextConfiguration註解中的路徑是Spring配置檔案的位置。測試的方法必須是public的,且沒有返回值。
mockito
mockito是一個用於模擬物件的工具,我認為他也是測試工作中必不可少的一部分,詳細的介紹我推薦可以看一下:
人生苦短,我用Mockito https://zhuanlan.zhihu.com/p/59275373
比較不錯的入門案例,它的maven座標地址為:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency>
Mock這種測試方法, 對比傳統的Junit測試,有如下好處:
- 不用每次測試的是時候,都初始化Spring容器,採用Mock的方式模擬物件,效率高
- 物件間的依賴關係,可以用Mock去表達,同時,我們不關心的部分,我們都可以用Mock的方式代替(比如物件A引用物件B的某某方法,但是我們不關係物件B方法實現,只想藉助方法,這個時候就可以Mock)
- 可以應對複雜的測試環境,比如方法呼叫順序、方法呼叫次數等等。
以下是Mock的一個小案例:
@RunWith(MockitoJUnitRunner.class) public class MockitoTest { /** * mock物件 */ @Mock List<String> mockedList; @Before public void setUp() { MockitoAnnotations.initMocks(this); } @Test public void testMock() { // mock物件行為 Mockito.when(mockedList.get(0)).thenReturn("one"); Assert.assertEquals("one", mockedList.get(0)); // 僅僅是mock了物件的行為,實際上列表還是空的 Assert.assertEquals(0, mockedList.size()); //驗證mock物件的get方法被呼叫過,且呼叫時的引數是0 Mockito.verify(mockedList).get(0); } }
這裡在使用@Mock的時候,必須事先呼叫MockitoAnnotations.initMocks(this),且使用@RunWith(MockitoJUnitRunner.class)
Jacoco
JaCoCo是一個開源的覆蓋率工具,支援多種覆蓋率的統計,其中包括:
- 行覆蓋率:度量被測程式的每行程式碼是否被執行,判斷標準行中是否至少有一個指令被執行。
- 類覆蓋率:度量計算class類檔案是否被執行。
- 分支覆蓋率:度量if和switch語句的分支覆蓋情況,計算一個方法裡面的總分支數,確定執行和不執行的 分支數量。
- 方法覆蓋率:度量被測程式的方法執行情況,是否執行取決於方法中是否有至少一個指令被執行。
- 指令覆蓋:計數單元是單個java二進位制程式碼指令,指令覆蓋率提供了程式碼是否被執行的資訊,度量完全 獨立原始碼格式。
- 圈複雜度:在(線性)組合中,計算在一個方法裡面所有可能路徑的最小數目,缺失的複雜度同樣表示測 試案例沒有完全覆蓋到這個模組。
下面是它的maven座標:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency>
接下來我們用maven外掛的方式,對jacoco進行配置
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.3</version> <configuration> <includes> <include>com/**/*</include> </includes> <!-- rules裡面指定覆蓋規則 --> <rules> <rule implementation="org.jacoco.maven.RuleConfiguration"> <element>BUNDLE</element> <limits> <!-- 指定方法覆蓋到50% --> <limit implementation="org.jacoco.report.check.Limit"> <counter>METHOD</counter> <value>COVEREDRATIO</value> <minimum>0.50</minimum> </limit> <!-- 指定分支覆蓋到50% --> <limit implementation="org.jacoco.report.check.Limit"> <counter>BRANCH</counter> <value>COVEREDRATIO</value> <minimum>0.50</minimum> </limit> <!-- 指定類覆蓋到100%,不能遺失任何類 --> <limit implementation="org.jacoco.report.check.Limit"> <counter>CLASS</counter> <value>MISSEDCOUNT</value> <maximum>0</maximum> </limit> </limits> </rule> </rules> </configuration> <executions> <execution> <id>pre-test</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>post-test</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin>
這裡值得注意的是<include>com/**/*</include>指的是class檔案的位置。做完這些以後,我們就可以生成報表了。因為我們是用maven外掛的方式進行配置的,所以如果我們使用idea進行開發的時候,就可以看到右側maven一欄中出現了jacoco外掛
最常用的就是這兩個,一個是檢查配置是否正確,第二個是用來將exec檔案,生成index.html用來進行觀察覆蓋率。
我們先執行maven中的test指令,這時,我們在target中就可以看到一個jacoco.exec檔案。
有了這個jacoco.exec檔案,就可以使用jacoco的report方法,來生成檔案。
右鍵index.html檔案,選擇Reveal in Finder(Mac),windows也是類似,開啟檔案磁碟的位置。
可以看到,由於這個專案之前沒有幾個單測,所以覆蓋率特別低。點開之後,就可以看到具體的程式碼,非常的方便。
最後今天配置jacoco的時候,踩了2個坑:
1 用idea進行開發的同學。使用jacoco的時候,不要勾選這個按鈕,它會跳過你測試階段的程式碼執行,進而不會生成jacoco.exec檔案。
2 保證自己測試程式碼沒有錯誤(尤其是專案中,由於程式碼更新,測試用例沒有更新,導致的測試不可用)
這裡的現象是雖然可以生成jacoco.exec 檔案,而且可以report成文件,但是開啟之後發現,程式碼覆蓋率都是0。
最後:
希望大家都可以保持寫測試用例的好習慣,謝謝
&n