一、爲何要用mockjava
個人一本書的解釋:數據庫
(1)建立所需的DB數據可能須要很長時間,如:調用別的接口,模擬不少數據api
(2)調用第三方API接口,測試很慢,網絡
(3)編寫知足全部外部依賴的測試可能很複雜,複雜到不值得編寫,Mock模擬內部或外部依賴能夠幫助咱們解決這些問題框架
另外一本TDD書的解釋:ide
(1)對象的結果不肯定,如每獲取當前時間,獲得的結果都不同,沒法符合咱們的預期;微服務
(2)實現這個接口的對象不存在;工具
(3)對象速度緩慢測試
對於TDD還有一個更重要緣由:經過模擬能夠隔離當前方法使用的的全部依賴,讓咱們更加專一於單個單元,忽略其調用的代碼的內部工做原理ui
一本博客的乾貨:
(1)Mock能夠用來解除測試對象對外部服務的依賴(好比數據庫,第三方接口等),使得測試用例能夠獨立運行。不論是傳統的單體應用,仍是如今流行的微服務,這點都特別重要,由於任何外部依賴的存在都會極大的限制測試用例的可遷移性和穩定性。
(2)Mock的第二個好處是替換外部服務調用,提高測試用例的運行速度。任何外部服務調用至少是跨進程級別的消耗,甚至是跨系統、跨網絡的消耗,而Mock能夠把消耗下降到進程內。好比原來一次秒級的網絡請求,經過Mock能夠降至毫秒級,整整3個數量級的差異。
(3)Mock的第三個好處是提高測試效率。這裏說的測試效率有兩層含義。第一層含義是單位時間運行的測試用例數,這是運行速度提高帶來的直接好處。而第二層含義是一個測試人員單位時間建立的測試用例數。
以單體應用爲例,隨着業務複雜度的上升,爲了運行一個測試用例可能須要準備不少測試數據,與此同時還要儘可能保證多個測試用例之間的測試數據互不干擾。爲了作到這一點,測試人員每每須要花費大量的時間來維護一套可運行的測試數據。有了Mock以後,因爲去除了測試用例之間共享的數據庫依賴,測試人員就能夠針對每個或者每一組測試用例設計一套獨立的測試數據,從而很容易的作到不一樣測試用例之間的數據隔離性。而對於微服務,因爲一個微服務可能級聯依賴不少其餘的微服務,運行一個測試用例甚至須要跨系統準備一套測試數據,若是沒有Mock,基本上能夠說是不可能的。所以,不論是單體應用仍是微服務,有了Mock以後,QE就能夠省去大量的準備測試數據的時間,專一於測試用例自己,天然也就提高了單人的測試效率。
現現在比較流行的Mock工具如jMock 、EasyMock 、Mockito等都有一個共同的缺點:不能mock靜態、final、私有方法等。而PowerMock可以完美的彌補以上三個Mock工具的不足
二、實戰:
好了,咱們用PoweMockito框架,直接上代碼:如何mock私有方法,靜態方法,測試私有方法,final類
依賴:
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>1.7.4</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.7.4</version> <scope>test</scope> </dependency>
powermock可能與mockito有版本衝突,咱們能夠講mockito版本改爲2.8.47 :
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <mockito.version>2.8.47</mockito.version> </properties>
service層代碼:一個簡單的保存,返回user,這裏就不調用持久層了
@Override public User save(User user) { return user; }
controller層代碼:一些簡單的調用,接下來寫個test 調用controller方法,mock service裏面的保存,mock controller的私有方法和靜態方法,測試controller的private的私有方法
package com.example.demo.controller; import com.example.demo.entity.User; import com.example.demo.service.UserService; public final class UserController { private final UserService service; public UserController(UserService service) { this.service = service; } public User saveUser(User user) { User save = service.save(user); return save; } public String returnName(){ return getStaticName("ljw1"); } public static String getStaticName(String name) { return "A_" + name; } public String getPrivateName(String name) { if (publicCheck()){ return "public 被mock 了"; } if (check(name)){ return "private 被mock 了"; } return "A_" + name; } public boolean publicCheck() { return false; } private boolean check(String name) { return false; } private String say(String content) { return "ljw say " + content; } }
test:測試,註釋寫的很清楚,把代碼拷貝過去,就能夠用了,我已經驗證過,user類就一個字段id,不附代碼了,注意:測試private方法和mock private方法的區別
package com.example.demo.demo1; import com.example.demo.controller.UserController; import com.example.demo.entity.User; import com.example.demo.service.impl.UserServiceImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.powermock.api.mockito.PowerMockito.when; @RunWith(PowerMockRunner.class) @PrepareForTest(UserController.class) public class LastMock { @Mock private UserServiceImpl serviceImpl; @InjectMocks private UserController controller; /** * mock service的保存方法 */ @Test public void mockSave() { User user1 = new User(); User user2 = new User(); user1.setId("1"); user2.setId("2"); Mockito.when(serviceImpl.save(user1)).thenReturn(user2); //當調用service的save()時,mock讓他返回user2 User saveUser = controller.saveUser(user1); //調用 Mockito.verify(serviceImpl,Mockito.times(1)).save(user1);//verify驗證mock次數 assertEquals(user2, saveUser);//斷言是否mock返回的是user2 } /** * mock spy public方法 * @throws Exception xx */ @Test public void spy_public_method() throws Exception { UserController spy = PowerMockito.spy(controller); //監視controller的publicCheck方法,讓他返回true Mockito.when(spy.publicCheck()).thenReturn(true); String name = spy.getPrivateName("ljw");//執行該方法 assertEquals("public 被mock 了", name);//驗證 } /** * mock私有方法 * @throws Exception xx */ @Test public void spy_private_method() throws Exception { UserController spy = PowerMockito.spy(controller); PowerMockito.when(spy, "check", any()).thenReturn(true);//私有方法mockito不行了,須要用無所不能的PowerMock監視spy String name = spy.getPrivateName("ljw"); assertEquals("private 被mock 了", name); } /** * mock 靜態方法 */ @Test public void mockStaticMethod() { PowerMockito.mockStatic(UserController.class);//mock靜態方法 when(UserController.getStaticName(any())).thenReturn("hi"); String staticName = UserController.getStaticName("ljw");//執行 assertEquals("hi", staticName);//驗證 } @Test public void mockStaticMethod_2() { PowerMockito.mockStatic(UserController.class); when(UserController.getStaticName(any())).thenReturn("hi"); String staticName = controller.returnName();//經過returnName()調用,看可否被mock assertEquals("hi", staticName); } /** * 測試私有方法一 * @throws InvocationTargetException xx * @throws IllegalAccessException xx */ @Test public void testPrivateMethod() throws InvocationTargetException, IllegalAccessException { Method method = PowerMockito.method(UserController.class, "say", String.class); Object say = method.invoke(controller, "hi"); assertEquals("ljw say hi", say); } /** * 測試私有方法二 * @throws Exception xx */ @Test public void testPrivateMethod_2() throws Exception { Object say = Whitebox.invokeMethod(controller, "say", "hi"); assertEquals("ljw say hi", say); } }