Mockito升级

news/2025/1/8 8:11:54/

背景

系统是SpringBoot的,原来单元测试用的 Mockito,不过由于版本较低,对静态方法无法Mock,所以又引入了 PowerMock;

好长时间没动过了,现在刚好有一个项目需要写测试代码,顺便也把Mockito升升级;

现在Mockito版本已经到了 5.2.0了,不过需要Java11的支持;目前项目还使用的是Java8,因此只能使用到 Mockito4,最高版本为 4.11.0;

而且从Mockito3的某个版本开始,已经支持了 静态方法的Mock,所以可以暂时抛弃PowerMock了;

依赖:

            <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>${spring-boot.version}</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>${mockito-version}</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-inline</artifactId><version>${mockito-version}</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>${mockito-version}</version><scope>test</scope></dependency><mockito-version>4.11.0</mockito-version>

其实没必要依赖这么多artifact,也没细究,看例子这样service和http都可以进行Mock测试;

Mock整个类

ClassA,调用到 ClassB , 现在自己Mock生成一个ClassB;

@Component
public class ClassA {@AutowiredClassB classB;public void printA(int value ) {int a;a = value + classB.getValue();System.out.println("------------------------------------------------------");System.out.println("ClassA : a value is " + a);System.out.println("------------------------------------------------------");}}
@Component
public class ClassB {public int getValue() {return 10;}}

测试代码:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest {@AutowiredClassA classA;@MockBeanClassB classB;@Testpublic void Simple() {when(classB.getValue()).thenReturn(5);classA.printA(5);}
}

如果没有Mock掉ClassB,

classA.printA(5) 的结果应该是: 5 + 10 ,结果是 15;

 Mock了ClassB,classB.getValue返回从10变成了5,结果应该显示10了

Mock一些方法

有时候需要使用到原始的类,但是只对其中某些方法进行Mock:

@Component
public class ClassA {@AutowiredClassB classB;public int printA(int value ) {int a;a = value + classB.getValue();return a;}public int printB(int value ) {int b;b = value + classB.getValue();return b;}}

正常情况下

@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest2 {@AutowiredClassA classA;@Testpublic void Simple() {System.out.println("printA : " + classA.printA(5) );System.out.println("printB : " + classA.printB(5) );}}

printA(5) 和 printB(5) 结果应该是一样的:

 

如果想Mock掉ClassA.printA, 让printA返回指定的值,那就需要对printA做处理:

在对printA函数做处理之前,首先要对 classA类进行包装:

方法1 :

    @AutowiredClassA classA;改变成:@SpyBeanClassA classA;

 或者:

@AutowiredClassA classA;@Testpublic void Simple() {classA = Mockito.spy(classA);。。。。。。}

然后对printA进行处理:

    @Testpublic void Simple() {classA = Mockito.spy(classA);when(classA.printA(anyInt())).thenReturn(100);System.out.println("printA : " + classA.printA(5) );System.out.println("printB : " + classA.printB(5) );}

结果:

其中: 

when(classA.printA(anyInt())).thenReturn(100);也可以写成doReturn(100).when(classA).printA(anyInt());

静态方法的Mock

现在的Mockito也可以对静态方法做Mock

Bean类:

@Component
public class ClassB {public int getValue() {return 10;}public static int getStaticInt() {return 1;}
}

这里ClassB,多了一个静态方法: getStaticInt 

在ClassA中,使用这个静态方法:

@Component
public class ClassA {public int invokeClassBStatic(int value ) {return value + ClassB.getStaticInt();}}

正常情况下,

@SpringBootTest
@RunWith(SpringRunner.class)
public class SimpleTest3 {@AutowiredClassA classA;@Testpublic void Simple() {classA.invokeClassBStatic(5);}
}

的结果是:

现在Mock掉ClassB. getStaticInt,让它返回100,代码变成:

@SpringBootTest
@RunWith(SpringRunner.class)
public class SimpleTest3 {@AutowiredClassA classA;@Testpublic void Simple() {MockedStatic<ClassB> classBMockedStatic = Mockito.mockStatic(ClassB.class);classBMockedStatic.when(() -> ClassB.getStaticInt()).thenReturn(100);System.out.println(classA.invokeClassBStatic(5));}
}

执行结果:

 

对void函数进行Mock

用 doNoting().when()

用新的函数Mock掉老函数

对printA进行Mock

@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest4 {@SpyBeanClassA classA;@Testpublic void Simple() {classA = Mockito.spy(classA);when(classA.printA(anyInt())).thenAnswer((invocation) -> {int p =  invocation.getArgument(0);return p * 2;});System.out.println("printA : " + classA.printA(5));System.out.println("printB : " + classA.printB(5));}}

结果:

 

被Mock的对象执行原方法:

对于mock的对象,它的所有方法都会被打桩,不执行真实方法,除非使用doCallRealMethod。

Foo mock = mock(Foo.class);
doCallRealMethod().when(mock).someVoidMethod();// 会执行实现方法 Foo.someVoidMethod()
mock.someVoidMethod();

调用原始方法,替换原始方法

ArgumentCaptor<Object> arg1 = ArgumentCaptor.forClass(Object.class);
ArgumentCaptor<Long> arg2 = ArgumentCaptor.forClass(Long.class);// doNothing忽略方法调用,并把方法的2个参数进行捕获
Mockito.doNothing().when(businessService).noReturnMethod1(arg1.capture(), arg2.capture());// 方法调用
String realArg1 = "我是参数1";
long realArg2 = 123567;
businessService.noReturnMethod1(realArg1, realArg2);// 对捕获的参数进行断言
Assert.isTrue(realArg1.equals(arg1.getValue()), "");
Assert.isTrue(realArg2 == arg2.getValue(), "");// void方法测试2,替换void方法
Mockito.doAnswer(invocation -> {Object objArg = invocation.getArgument(1);Long longArg = invocation.getArgument(0);System.out.println(objArg + "===" + longArg);// 对捕获的参数进行断言Assert.isTrue(realArg1.equals(objArg), "");Assert.isTrue(realArg2 == longArg, "");return invocation.callRealMethod();// 需要时,这里可以回调原始方法
}).when(businessService).noReturnMethod2(ArgumentMatchers.anyLong(), ArgumentMatchers.any());businessService.noReturnMethod2(realArg2, realArg1);


http://www.ppmy.cn/news/34416.html

相关文章

【新星计划2023】SQL SERVER (01) -- 基础知识

【新星计划2023】SQL SERVER -- 基础知识1. Introduction1.1 Official Website1.2 Conn Tool2. 基础命令2.1 建库建表2.2 Alter2.3 Drop2.3 Big Data -- Postgres3.Awakening1. Introduction 1.1 Official Website 官方文档&#xff08;小技巧&#xff09; Officail Website: …

一线大厂软件测试常见面试题1500问,背完直接拿捏面试官,

三、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段&#xff1a;需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的SE会把需求文档给我们自己先去了解一到两天这样&#xff0c;之后我们会有一个需求澄清会议&#xff0c; 我…

【STM32】STM32内存映射以及启动过程(超详细过程)

一、内存映射 1、内存映射图 下图是 STM32F103xCDE 型号的内存映射图。 2、内存划分 由于 STM32 是 32 位&#xff0c;且其地址总线也为 32 根&#xff0c;所以其理论能够寻找的地址大小为 4GB。 从上图可以看出&#xff0c;左边的地址从 0x0000 0000 ~ 0xFFFF FFFF 的 4GB…

大数据 | Hadoop集群搭建(完全分布式)

知识目录一、前言二、配置三台虚拟机2.1 克隆三台虚拟机2.2 配置克隆的虚拟机2.3 使用Xshell连接虚拟机2.4 配置SSH免密登录三、Hadoop集群准备3.1 安装 rsync3.2 安装xsync分发脚本3.3 安装JDK和安装Hadoop3.4 配置环境变量3.5 分发四、Hadoop集群搭建4.1 修改配置文件4.2 配置…

(五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置

前言 本节内容我们实现虚拟机的克隆&#xff0c;主要根据模板虚拟机克隆三台hadoop虚拟机&#xff0c;用于hadoop集群的搭建&#xff0c;同时根据上一小节的内容&#xff0c;配置hadoop虚拟机的主机名、ip网络等&#xff0c;最终完成hadoop虚拟机的实例化。 正文 虚拟机克隆…

机器学习-scikit-learn

文章目录前言线性回归模型-LinearRegression准备数据集使用LinearRegression总结前言 scikit-learn是Python中最流行的机器学习库之一&#xff0c;它提供了各种各样的机器学习算法和工具&#xff0c;包括分类、回归、聚类、降维等。 scikit-learn的优点有&#xff1a; 简单易…

去中心化联邦学习思想

去中心化联邦学习是一种保护用户隐私的分散式机器学习方法。与集中式联邦学习相比&#xff0c;去中心化联邦学习更加注重保护用户数据隐私&#xff0c;同时也更具有扩展性和健壮性。 在去中心化联邦学习中&#xff0c;每个设备都使用本地数据进行模型训练&#xff0c;并将模型…

算法训练营第五十九天|LeetCode647、516

题目连接&#xff1a;647. 回文子串 - 力扣&#xff08;LeetCode&#xff09;个人思路&#xff1a;dp数组的含义是&#xff1a;dp[i][j]&#xff1a;s字符串下标i到下标j的字串是否是一个回文串这里我出现了错误为什么出错呢&#xff1f;代码如下&#xff1a;class Solution {p…