說說初用 Mock 工具測試碰到的坑
我是一個在校實習生,作為一個程式猿,是個菜鳥中戰鬥機!對於測試,只寫過一點點簡單到不能再簡單了的 Junit 單元測試的例子(因為當時這足以應付學校課程的內容與要求)。這幾天在公司裡要真槍實彈做測試的時候,就深深體會到了“書到用時方恨少”這句話的真諦了。沒辦法,誰叫我當初不多深入的學點呢。於是,在杜叔杜大神的指導下,開始了菜鳥的初用 Mock 工具,邊學邊用的爬坑之旅。
為什麼要用 Mock 工具?
話不多說,我們先直接看看個最簡單的例子:
測試目標:
public class ToBeTested {
public int add(int a, int b) {
return a+b;
}
public int plus(int a, int b){
return a*b; } }
測試用例:
public class test {
@Test
public void testEquals(){
ToBeTested t = new ToBeTested();
assertEquals(3,t.add(1,2));
assertEquals(8,t.plus(2,4)); } }
這裡我們是直接通過 new 來構建了一個 ToBeTested 的例項,因為這個類簡單,而且待測試的方法裡也沒有依賴任何外部的物件,就一個簡單的加法或乘法就完事了。但事情總是沒有那麼簡單,在做單元測試的時候,我們要測試的方法往往都是需要依賴很多外部的物件,比如網路通訊,遠端服務之類的,這些外部物件是我麼沒法控制的。難道這些外部依賴的物件都需要 new 一個出來嗎 NO! 我們有 Mock! 我們可以用 Mock 工具來模擬這些外部物件,來完成我們的單元測試。還是先來看一個簡單的 Mock 測試的例子吧:
public class test {
@Test
public void test(){
List<String> list = Mockito.mock(List.class);
list.add("coding");
verify(list).add("coding"); } }
坑1 : spy
如果要用真實物件(而不是 Mock 出來的虛擬物件)中的真實的方法,則需要 spy 一下!看下面的例子:
我在測試目的碼中加了一個 compute 函式:
public int compute(int a){
int b = add(a,a);
int c = plus(a,a);
return b+c;
}
隨後想當然的寫了個對應的測試用例:
@Test
public void testCompute(){
ToBeTested t = new ToBeTested();
assertEquals(8, t.compute(2));
}
但聰明的你一看就知道,這不叫單元測試。因為我只是想測試 compute 這個函式是不是正確的,但 compute 卻依賴於 add 和 plus 這兩個函式。如果 add 函式裡有錯,也會導致 compute 出錯,可這根本不關 compute 的事啊。所以,測試用例程式碼應該是這樣的:
@Test
public void testCompute(){
ToBeTested t = spy(new ToBeTested());
when(t.add(2,2)).thenReturn(4);
when(t.plus(2, 2)).thenReturn(4);
assertEquals(8, t.compute(2)); }
注意!這裡 new 一個真實物件 t 的時候,需要 spy 一下!(當時年輕不懂事,被這裡卡了略久。。)因為這裡用到了依賴的方法 add 和 plus。現在對於這個例子,compute 的結果已經不依賴別人了,哪怕 add 方法裡,不知是哪個粗心的程式猿寫成了 “return a - b ",這個測試也是通過的,因為這裡有 when(t.add(2,2)).thenReturn(4);
這句話,stubbing 了 add 這個方法。這也符合單元測試的概念,我們現在只負責測試 compute 這個函式,才不管 add 或 plus 正確與否呢。
坑2:doReturn
等等,如剛剛所說,compute 已經不依賴 add 的返回結果了,那是不是當我們測試 compute 函式的時候,add 函式是不是就可以完全無法無天的亂來了呢?我們來看看這個:
public int add(int a, int b) {
ArrayList<String> list = new ArrayList<String>();
String s = list.get(0);
return a+b;
}
這時,當我們再次執行測試時,就拋異常了:java.lang.IndexOutOfBoundsException
。顯然 list.get(0)
是罪魁禍首!奇怪?不是說 compute 已經不管 add 了嗎? add 裡面的程式碼有問題又怪我咯?別忘了,這還是 java 語言!所以,when(t.add(2,2)).thenReturn(4);
這語句還是會先去執行一遍 when 裡面的函式, add(2,2). 至於返回結果是另外一回事。那麼怎麼解決這個問題呢?對!用 doReturn 語句!
@Test
public void testCompute(){
ToBeTested t = spy(new ToBeTested());
//when(t.add(2,2)).thenReturn(4);
doReturn(4).when(t).add(2,2);
when(t.plus(2, 2)).thenReturn(4); ; assertEquals(8, t.compute(2)); }
現在好了, compute 已經完全不受 add 函式的影響了。add 函式愛怎麼瘋就怎麼瘋,不會影響 compute 函式的測試結果了,至此, compute 終於可以安靜地完成它的單元測試了。
坑3: PowerMock
現如今比較流行的 Mock 工具如 jMock、EasyMock、Mockito 等都有一個共同的缺點:不能 mock 靜態、final、私有方法等。而 PowerMock 能夠完美的彌補以上三個 Mock 工具的不足。至於怎麼做,去 Google 一下一大把的教程與例項,這裡就不再多說了。。
坑4,5,6,8,。。。。。
慢慢踩 :)
from: https://blog.coding.net/blog/mock-testing-tools