Mockito如何mock靜態(tài)方法問題
Mockito如何mock靜態(tài)方法
在實際工作當中,我們經常會遇到需要對靜態(tài)方法進行 mock 的情況。在 mockito 2.x 的時代,我們需要借助 powmock 才能實現(xiàn)。
當 mockito 進化到了 3.4.0 版本以后,也開始對靜態(tài)方法 mock 進行了支持(主要是通過 mockito-inline 包)。
簡單的介紹就到這里,下面讓我們進入主題吧。
1.首先確保pom文件中
mockito 相關 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 內提供了靜態(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");
...
// 關閉
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” 進行詳細說明)
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
當多個單元測試都使用了同一個 static mock 對象,且使用完成后都沒有進行 close。此時,若這幾個單元測試用一起執(zhí)行,第一個單元測試占用了 static mock 對象,第二個單元測試就沒有辦法再占用了。
如果出現(xià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),且覺得在每個單元測試當中都寫一遍 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");
...
}這樣就清爽多了 ~:)
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SpringCloud_Sleuth分布式鏈路請求跟蹤的示例代碼
Spring Cloud Sleuth是一款針對Spring Cloud的分布式跟蹤工具,本文通過實例代碼介紹了SpringCloud_Sleuth分布式鏈路請求跟蹤,感興趣的朋友跟隨小編一起看看吧2023-02-02
基于Springboot實現(xiàn)送水公司信息管理系統(tǒng)
這篇文章主要介紹了基于Springboot實現(xiàn)送水公司信息管理,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-01-01
SpringBoot自定義Starter與自動配置實現(xiàn)方法詳解
在Spring Boot官網為了簡化我們的開發(fā),已經提供了非常多場景的Starter來為我們使用,即便如此,也無法全面的滿足我們實際工作中的開發(fā)場景,這時我們就需要自定義實現(xiàn)定制化的Starter2023-02-02
關于微服務使用Dubbo設置的端口和server.port的區(qū)別
這篇文章主要介紹了關于微服務使用Dubbo設置的端口和server.port的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12

