Mockito 如何 mock 静态方法

本文介绍了如何在Mockito 3.4.0及以上版本中对静态方法进行模拟,包括在pom.xml中引入mockito-core和mockito-inline依赖,以及使用MockedStatic进行静态方法mock的示例。同时,文章讨论了不关闭静态mock对象导致的错误及其解决方案,提倡在每个测试方法中使用完静态mock后调用close()方法,或者在测试类的生命周期方法中进行管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在实际工作当中,我们经常会遇到需要对静态方法进行 mock 的情况。在 mockito 2.x 的时代,我们需要借助 powmock 才能实现。当 mockito 进化到了 3.4.0 版本以后,也开始对静态方法 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 内提供了静态方法
    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,会出现如下错误信息:

      org.mockito.exceptions.base.MockitoException: 
      The used MockMaker SubclassByteBuddyMockMaker does not support the creation of static mocks
      
      Mockito'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。此时,若这几个单元测试用一起执行,第一个单元测试占用了 static mock 对象,第二个单元测试就没有办法再占用了。

      如果出现了这种情况,解决办法也很简单,就是关闭 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;
      
      	// 每个单元测试启动前,先执行该方法(高版本中 @Before 被替换成 @BeforeEach)
          @Before
          public void setUp() {
              this.httpClient = mockStatic(HTTPClient.class);
          }
          
      	// 每个单元测试执行完成后,执行该方法(高版本中 @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");
              
              ...
          }
      

      这样就清爽多了 ~:)

org.mockito.exceptions.base.MockitoException是一个Mockito框架的异常,它表示在使用Mockito进行单元测试时出现了问题。根据引用\[1\]和引用\[2\],这个异常可能是由于使用了不支持创建静态模拟对象的MockMaker或者在当前线程中已经注册了静态模拟对象引起的。而根据引用\[3\],这个异常也可能是由于无法模拟或者监视final类引起的。 要解决这个异常,可以尝试以下几种方法: 1. 检查使用的MockMaker是否支持创建静态模拟对象。如果不支持,可以尝试使用其他支持静态模拟对象的MockMaker。 2. 检查当前线程是否已经注册了静态模拟对象。如果是,可以尝试在使用之前取消注册或者在新的线程中进行测试。 3. 如果异常是由于无法模拟或者监视final类引起的,可以尝试使用PowerMock等其他工具来处理final类的模拟或者监视。 需要根据具体情况来选择适合的解决方法。 #### 引用[.reference_title] - *1* *2* [Mockito -- 如何Mock Util类中的static 方法?](https://blog.csdn.net/qq_41055045/article/details/126977753)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Mockito与PowerMock使用问题:Argument should be a mock, but is: class java.lang.Class](https://blog.csdn.net/qq_36059826/article/details/109319531)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cab5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值