Mockito如何mock静态方法问题

 更新时间:2025年02月25日 10:53:16   作者:cab5  
本文介绍了如何在Mockito 3.4.0版本及以上版本中mock静态方法,包括如何在pom文件中添加相关jar包、如何使用Mockito.mockStatic(Class mockClass)方法以及注意事项等

Mockito如何mock静态方法

在实际工作当中,我们经常会遇到需要对静态方法进行 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");
        
        ...
    }

这样就清爽多了 ~:)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 深入了解JVM(Java虚拟机)内存结构

    深入了解JVM(Java虚拟机)内存结构

    Java虚拟机(Java Virtual Machine,JVM)是Java程序的运行环境,它是一个抽象的计算机模型,通过解释和执行Java字节码来运行Java程序,本将大家深入了解JVM(Java虚拟机)内存结构,需要的朋友可以参考下
    2023-08-08
  • Tomcat调优详解

    Tomcat调优详解

    这篇文章主要介绍了Tomcat调优方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • 详解lambda表达式foreach性能分析

    详解lambda表达式foreach性能分析

    这篇文章主要介绍了详解lambda表达式foreach性能分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • java基础中异常及包归纳整理

    java基础中异常及包归纳整理

    这篇文章主要介绍了java基础中的 异常与包,有需要的朋友可以参考一下
    2017-04-04
  • Linux系统下安装和卸载JDK8的方式

    Linux系统下安装和卸载JDK8的方式

    这篇文章主要介绍了Linux安装和卸载JDK8,第一种是使用yum命令一键安装,默认安装目录在/usr/lib/jvm第二种是手动安装,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • springboot部署linux访问服务器资源的方法

    springboot部署linux访问服务器资源的方法

    这篇文章主要介绍了springboot部署linux访问服务器资源,部署springboot项目至服务器用了几种不同方法,文中给大家详细介绍,需要的朋友可以参考下
    2019-12-12
  • 聊聊spring boot的WebFluxTagsProvider的使用

    聊聊spring boot的WebFluxTagsProvider的使用

    这篇文章主要介绍了聊聊spring boot的WebFluxTagsProvider的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • SpringMVC RESTFul实体类创建及环境搭建

    SpringMVC RESTFul实体类创建及环境搭建

    这篇文章主要为大家介绍了SpringMVC RESTFul实体类创建及环境搭建详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Java编程中的vector类用法学习笔记

    Java编程中的vector类用法学习笔记

    Vector通常被用来实现动态数组,即可实现自动增长的对象数组,和C++一样vector类同样被Java内置,下面就来看一下vector类的基本用法.
    2016-05-05
  • RocketMQ NameServer 核心源码解析

    RocketMQ NameServer 核心源码解析

    这篇文章主要为大家介绍了RocketMQ NameServer 核心源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09

最新评论