Mockito如何mock靜態(tài)方法問題
Mockito如何mock靜態(tài)方法
在實際工作當(dāng)中,我們經(jīng)常會遇到需要對靜態(tài)方法進(jìn)行 mock 的情況。在 mockito 2.x 的時代,我們需要借助 powmock 才能實現(xiàn)。
當(dāng) mockito 進(jìn)化到了 3.4.0 版本以后,也開始對靜態(tài)方法 mock 進(jìn)行了支持(主要是通過 mockito-inline 包)。
簡單的介紹就到這里,下面讓我們進(jìn)入主題吧。
1.首先確保pom文件中
mockito 相關(guān) jar 包的版本(這里我用的版本是 3.7.7),如下:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.7.7</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>3.7.7</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>3.7.7</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency>
2.使用方式
Mockito.mockStatic(Class mockClass),如下:
// 這里 DateUtil 內(nèi)提供了靜態(tài)方法 MockedStatic<DateUtil> dateUtil = mockStatic(DateUtil.class);
示例:
import static org.mockito.Mockito.mockStatic; @RunWith(MockitoJUnitRunner.class) public class AlphaServiceTest { @Test public void testHttp() { ... MockedStatic<HTTPClient> httpClient = mockStatic(HTTPClient.class); httpClient.when(() -> HTTPClient.sendPost("xxx/zzz/ccc", "hello")).thenReturn("success"); ... // 關(guān)閉 httpClient.close(); }
這樣基本上就 OK 了。
唯一需要注意下的就是 httpClient.close(),這個問題會在「3.其他」中 “錯誤提示 static mocking is already registered in the current thread To create a new mock, the existing static mock registration must be deregistered” 進(jìn)行詳細(xì)說明)
3.其他
如果項目中未引入 mockito-inline,會出現(xiàn)如下錯誤信息:
org.mockito.exceptions.base.MockitoException:
The used MockMaker SubclassByteBuddyMockMaker does not support the creation of static mocksMockito's inline mock maker supports static mocks based on the Instrumentation API.
You can simply enable this mock mode, by placing the 'mockito-inline' artifact where you are currently using 'mockito-core'.
Note that Mockito's inline mock maker is not supported on Android.at com.annoroad.order.service.PreOrderServiceTestCase.testSaveClinicalFreeSuccess1(PreOrderServiceTestCase.java:86)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
錯誤提示
static mocking is already registered in the current thread To create a new mock, the existing static mock registration must be deregistered
當(dāng)多個單元測試都使用了同一個 static mock 對象,且使用完成后都沒有進(jìn)行 close。此時,若這幾個單元測試用一起執(zhí)行,第一個單元測試占用了 static mock 對象,第二個單元測試就沒有辦法再占用了。
如果出現(xiàn)了這種情況,解決辦法也很簡單,就是關(guān)閉 static mock 對象,如下:
import static org.mockito.Mockito.mockStatic; @RunWith(MockitoJUnitRunner.class) public class AlphaServiceTest { @Test public void testHttp1() { ... MockedStatic<HTTPClient> httpClient = mockStatic(HTTPClient.class); httpClient.when(() -> HTTPClient.sendPost("xxx/zzz/ccc", "hello")).thenReturn("success"); ... httpClient.close(); } @Test public void testHttp2() { ... MockedStatic<HTTPClient> httpClient = mockStatic(HTTPClient.class); httpClient.when(() -> HTTPClient.sendPost("xxx/zzz/ccc", "hello")).thenReturn("success"); ... httpClient.close(); } @Test public void testHttp3() { ... MockedStatic<HTTPClient> httpClient = mockStatic(HTTPClient.class); httpClient.when(() -> HTTPClient.sendPost("xxx/zzz/ccc", "hello")).thenReturn("success"); ... httpClient.close(); }
如果你的很多單元測試中都用到了 mockStatic(HTTPClient.class),且覺得在每個單元測試當(dāng)中都寫一遍 mockStatic()…close() 很低效,可以采用下邊的方式:
import static org.mockito.Mockito.mockStatic; @RunWith(MockitoJUnitRunner.class) public class AlphaServiceTest { private MockedStatic<HttpClietn> httpClient; // 每個單元測試啟動前,先執(zhí)行該方法(高版本中 @Before 被替換成 @BeforeEach) @Before public void setUp() { this.httpClient = mockStatic(HTTPClient.class); } // 每個單元測試執(zhí)行完成后,執(zhí)行該方法(高版本中 @After 被替換成 @AfterEach) @After public void teardown() { this.httpClient.close(); } @Test public void testHttp1() { ... httpClient.when(() -> HTTPClient.sendPost("xxx/zzz/ccc", "hello")).thenReturn("success"); ... } @Test public void testHttp2() { ... httpClient.when(() -> HTTPClient.sendPost("xxx/zzz/ccc", "hello")).thenReturn("success"); ... } @Test public void testHttp3() { ... httpClient.when(() -> HTTPClient.sendPost("xxx/zzz/ccc", "hello")).thenReturn("success"); ... }
這樣就清爽多了 ~:)
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud_Sleuth分布式鏈路請求跟蹤的示例代碼
Spring Cloud Sleuth是一款針對Spring Cloud的分布式跟蹤工具,本文通過實例代碼介紹了SpringCloud_Sleuth分布式鏈路請求跟蹤,感興趣的朋友跟隨小編一起看看吧2023-02-02基于Springboot實現(xiàn)送水公司信息管理系統(tǒng)
這篇文章主要介紹了基于Springboot實現(xiàn)送水公司信息管理,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-01-01SpringBoot自定義Starter與自動配置實現(xiàn)方法詳解
在Spring Boot官網(wǎng)為了簡化我們的開發(fā),已經(jīng)提供了非常多場景的Starter來為我們使用,即便如此,也無法全面的滿足我們實際工作中的開發(fā)場景,這時我們就需要自定義實現(xiàn)定制化的Starter2023-02-02關(guān)于微服務(wù)使用Dubbo設(shè)置的端口和server.port的區(qū)別
這篇文章主要介紹了關(guān)于微服務(wù)使用Dubbo設(shè)置的端口和server.port的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12