一、目前應用比較普遍的java單元測試工具 junit4+Mock(Mockito /jmock / powermock)或Stub(用得較少,一般不推薦),由於junit3目前用得不多,基本升級到junit4了,所以就直接簡單說下junit4。
問題一:為什么需要mock或stub?它與junit什么關系?
在做單元測試的時候,我們會發現我們要測試的方法會引用很多外部依賴的對象,比如:(發送郵件,網絡通訊,記錄Log, 文件系統 之類的)。 而我們沒法控制這些外部依賴的對象。 為了解決這個問題,我們需要用到Stub和Mock來模擬這些外部依賴的對象,從而控制它們。
JUnit是單元測試框架,可以輕松的完成關聯依賴關系少或者比較簡單的類的單元測試,但是對於關聯到其它比較復雜的類或對運行環境有要求的類的單元測試,模擬環境或者配置環境會非常耗時,實施單元測試比較困難。而這些“mock框架”(Mockito 、jmock 、 powermock、EasyMock),可以通過mock框架模擬一個對象的行為,從而隔離開我們不關心的其他對象,使得測試變得簡單。(例如service調用dao,即service依賴dao,我們可以通過mock dao來模擬真實的dao調用,從而能達到測試service的目的。)
模擬對象(Mock Object)可以取代真實對象的位置,用於測試一些與真實對象進行交互或依賴於真實對象的功能,模擬對象的背后目的就是創建一個輕量級的、可控制的對象來代替測試中需要的真實對象,模擬真實對象的行為和功能。
問題二:mock與stub什么區別?
Mock和Stub是兩種測試代碼功能的方法。Mock測重於對功能的模擬,Stub測重於對功能的測試重現。比如對於List接口,Mock會直接對List進行模擬,而Stub會新建一個實現了List的TestList,在其中編寫測試的代碼。
強烈建議優先選擇Mock方式,因為Mock方式下,模擬代碼與測試代碼放在一起,易讀性好,而且擴展性、靈活性都比Stub好。
其中EasyMock和Mockito對於Java接口使用接口代理的方式來模擬,對於Java類使用繼承的方式來模擬(也即會創建一個新的Class類)。Mockito支持spy方式,可以對實例進行模擬。但它們都不能對靜態方法和final類進行模擬,powermock通過修改字節碼來支持了此功能。
有篇文章介紹:http://blog.csdn.net/devhubs/article/details/8018084
二、junit4相關介紹
這里有篇文章介紹了junit4的一些,包括怎么引入,使用,蠻詳細。---》 http://blog.csdn.net/happylee6688/article/details/38069761
這邊就記錄一些常用注解,當做學習方便。
常用注解
@Before:初始化方法,在任何一個測試方法執行之前,必須執行的代碼。對比 JUnit 3 ,和 setUp()方法具有相同的功能。在該注解的方法中,可以進行一些准備工作,比如初始化對象,打開網絡連接等。
同時,該 Annotation(@Test) 還可以測試期望異常和超時時間,如 @Test(timeout=100),我們給測試函數設定一個執行時間,超過這個時間(100毫秒),他們就會被系統強行終止,並且系統還會向你匯報該函數結束的原因是因為超時,這樣你就可以發現這些 bug 了。而且,它還可以測試期望的異常,例如,我們剛剛的那個空指針異常就可以這樣:@Test(expected=NullPointerException.class)。
@Ignore:忽略的測試方法,標注的含義就是“某些方法尚未完成,咱不參與此次測試”;這樣的話測試結果就會提示你有幾個測試被忽略,而不是失敗。一旦你完成了相應的函數,只需要把 @Ignore 注解刪除即可,就可以進行正常測試了。當然,這個 @Ignore 注解對於像我這樣有“強迫症”的人還是大有意義的。每當看到紅色條(測試失敗)的時候就會全身不舒服,感覺無法忍受(除非要測試的目的就是讓它失敗)。當然,對代碼也是一樣,無法忍受那些雜亂不堪的代碼。
@BeforeClass:針對所有測試,也就是整個測試類中,在所有測試方法執行前,都會先執行由它注解的方法,而且只執行一次。當然,需要注意的是,修飾符必須是 public static void xxxx ;此 Annotation 是 JUnit 4 新增的功能。
@AfterClass:針對所有測試,也就是整個測試類中,在所有測試方法都執行完之后,才會執行由它注解的方法,而且只執行一次。當然,需要注意的是,修飾符也必須是 public static void xxxx ;此 Annotation 也是 JUnit 4 新增的功能,與 @BeforeClass 是一對。
執行順序

三、Mock的幾種比較(Mockito 、jmock 、 powermock)
介紹文章一:http://blog.csdn.net/luvinahlc/article/details/10442743
介紹文章二:http://blog.csdn.net/zhangxin09/article/details/42422643
介紹文章三(Mockito 文檔):https://static.javadoc.io/org.mockito/mockito-core/2.8.47/org/mockito/Mockito.html
Spring提供了對Junit支持,可以使用注解的方式(注解加在需要測試的類上):
@RunWIth(SpringJunit4ClassRunner.class) ---->為了讓測試在Spring容器環境下執行
@ContextConfiguration(locations = {"classpath:applicationContext.xml"} --->用來指明Spring的配置文件位置
Mockito簡單運用說明
① when(mock.someMethod()).thenReturn(value):設定mock對象某個方法調用時的返回值。可以連續設定返回值,即when(mock.someMethod()).thenReturn(value1).then
Return(value2),第一次調用時返回value1,第二次返回value2。也可以表示為如下:
when(mock.someMethod()).thenReturn(value1,value2)。
② 調用以上方法時拋出異常: when(mock.someMethod()).thenThrow(new Runtime
Exception());
③ 另一種stubbing語法:
doReturn(value).when(mock.someMethod())
doThrow(new RuntimeException()).when(mock.someMethod())
④ 對void方法進行方法預期設定只能用如下語法:
doNothing().when(mock.someMethod())
doThrow(new RuntimeException()).when(mock.someMethod())
doNothing().doThrow(new RuntimeException()).when(mock.someMethod())
⑤ 方法的參數可以使用參數模擬器,可以將anyInt()傳入任何參數為int的方法,即anyInt匹配任何int類型的參數,anyString()匹配任何字符串,anySet()匹配任何Set。
⑥ Mock對象只能調用stubbed方法,調用不了它真實的方法,但是Mockito可以用spy來監控一個真實對象,這樣既可以stubbing這個對象的方法讓它返回我們的期望值,又可以使得對其他方法調用時將會調用它的真實方法。
⑦ Mockito會自動記錄自己的交互行為,可以用verify(…).methodXxx(…)語法來驗證方法Xxx是否按照預期進行了調用。
(1) 驗證調用次數:verify(mock,times(n)).someMethod(argument),n為被調用的次數,如果超過或少於n都算失敗。除了times(n),還有never(),atLease(n),atMost(n)。
(2) 驗證超時:verify(mock, timeout(100)).someMethod();
(3) 同時驗證:verify(mock, timeout(100).times(1)).someMethod();
相關注解:
MockitoAnnotations.initMocks(this);
initializes fields annotated with Mockito annotations.
- Allows shorthand creation of objects required for testing.
- Minimizes repetitive mock creation code.
- Makes the test class more readable.
- Makes the verification error easier to read because field name is used to identify the mock.
ReflectionTestUtils.setField(AopTargetUtils.getTarget(appInfoService), "openAppInfoMapper",openAppInfoMapperMock);
但是由於Spring可以使用@Autoware類似的注解方式,對私有的成員進行賦值,此時無法直接對私有的依賴設置mock對象。可以通過引入ReflectionTestUtils,解決依賴注入的問題。
@InjectMocks --- injects mock or spy fields into tested object automatically.
這個注解不會把一個類變成mock或是spy,但是會把當前對象下面的Mock/Spy類注入進去,按類型注入。
@Mock 生成的類,所有方法都不是真實的方法,而且返回值都是NULL。---> when(dao.getOrder()).thenReturn("returened by mock ");
@Spy ---Creates a spy of the real object. The spy calls real methods unless they are stubbed.
生成的類,所有方法都是真實方法,返回值都是和真實方法一樣的。---> doReturn("twotwo").when(ps).getPriceTwo();
Mockito可以完成對一般對象方法的模擬,但是對於靜態函數、構造函數、私有函數等還是無能為力.
代碼展示mock和spy的區別:
@Test public void test() { //test for Mock List mockList = Mockito.mock(List.class); mockList.add("1"); System.out.println(mockList.get(0));//返回null,說明並沒有調用真正的方法 Mockito.when(mockList.size()).thenReturn(100);//stub System.out.println(mockList.size());//size() method was stubbed,返回100 //test for Spy List list = new LinkedList(); List spy = Mockito.spy(list); //optionally, you can stub out some methods: Mockito.when(spy.size()).thenReturn(100); //using the spy calls real methods spy.add("one"); spy.add("two"); //prints "one" - the first element of a list System.out.println(spy.get(0)); //size() method was stubbed - 100 is printed System.out.println(spy.size()); }
import org.junit.Test; import org.mockito.Mockito; public class PasswordValidatorTest { private static final String PASSWORD_TRUE = "ck_is_handsome"; private static final String PASSWORD_FALSE = "ck_is_not_handsome"; // Test spy @Test public void testVerifyPassword() { //跟創建mock類似,只不過調用的是spy方法,而不是mock方法。spy的用法 PasswordValidator spyValidator = Mockito.spy(PasswordValidator.class); //在默認情況下,spy對象會調用這個類的真實邏輯,並返回相應的返回值,這可以對照上面的真實邏輯 System.out.println("[Real]" + PASSWORD_TRUE + " : " + spyValidator.verifyPassword(PASSWORD_TRUE)); //true System.out.println("[Real]" + PASSWORD_FALSE + " : " + spyValidator.verifyPassword(PASSWORD_FALSE)); //false //spy對象的方法也可以指定特定的行為 Mockito.when(spyValidator.verifyPassword(Mockito.anyString())).thenReturn(true); //同樣的,可以驗證spy對象的方法調用情況 System.out.println("[Mock]" + PASSWORD_TRUE + " : " + spyValidator.verifyPassword(PASSWORD_TRUE)); //true System.out.println("[Mock]" + PASSWORD_FALSE + " : " + spyValidator.verifyPassword(PASSWORD_FALSE)); //true Mockito.verify(spyValidator, Mockito.times(2)).verifyPassword(PASSWORD_TRUE); //pass } } class PasswordValidator { public boolean verifyPassword(String password) { return "ck_is_handsome".equals(password); } }
總之,spy與mock的唯一區別就是默認行為不一樣:spy對象的方法默認調用真實的邏輯,mock對象的方法默認什么都不做,或直接返回默認值。
轉自: https://www.cnblogs.com/wuyun-blog/p/7081548.html