1 简介
1.1 单元测试的特点
配合断言使用(杜绝System.out)
可重复执行
不依赖环境
不会对数据产生影响
spring的上下文环境不是必须的
一般都需要配合mock类框架来实现
1.2 mock类框架使用场景
要进行测试的方法存在外部依赖(如db,redis,第三方接口调用等),为了能够专注于对该方法(单元)的逻辑进行测试,就希望能虚拟出外部依赖,避免外部依赖成为测试的阻塞项,一般都是测试service层即可。
1.3 常用mock类框架
mock类框架:用于mock外部依赖
1.3.1 mockito
官网文档:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
限制:老版本对于final class、final method、static method、private method均不能被mockito mock,目前已支持final class、final method、static method的mock,具体可以参考官网
原理:bytebuddy
1.3.2 easymock
1.3.3 powermock
官网:https://github.com/powermock/powermock
与mockito的版本支持关系:https://gitee.com/mirrors/powermock/wikis/Mockito#supported-versions
对mockito或easymock的增强
1.3.4 JMockit
2 mockito的单独使用
2.0 环境准备
2.0.1 maven依赖
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
</parent>
<groupId>com.nico</groupId>
<artifactId>mockito</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mockito-demo</name>
<description>mockito-demo</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<!--springboot相关-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--spring-boot-starter-test中不包含junit4,需要单独引入-->
<!--junit4-->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<!--引入mockito,以下2个依赖在spring-boot-starter-test中已包含(4.0.0),所以不需要再单独引入-->
<!-- <dependency>-->
<!-- <groupId>org.mockito</groupId>-->
<!-- <artifactId>mockito-core</artifactId>-->
<!-- </dependency>-->
<!--mockito配合junit5使用才需要的依赖,junit4不需要引入-->
<!-- <dependency>-->
<!-- <groupId>org.mockito</groupId>-->
<!-- <artifactId>mockito-junit-jupiter</artifactId>-->
<!-- </dependency>-->
<!--mybatis相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<!--工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.9</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
2.0.2 数据库
sql
create database mockito_demo CHARACTER SET utf8mb4;
use mockito_demo;
drop table if exists user;
CREATE TABLE `user`
(
`id` bigint NOT NULL AUTO_INCREMENT comment '主键',
`username` varchar(100) NOT NULL comment '用户名称',
`phone` varchar(50) NOT NULL comment '电话',
PRIMARY KEY (`id`)
) ENGINE = InnoDB comment '用户表';
drop table if exists user_feature;
CREATE TABLE `user_feature`
(
`id` bigint NOT NULL AUTO_INCREMENT comment '主键',
`user_id` bigint NOT NULL comment '用户id:用户表的主键',
`feature_value` varchar(150) NOT NULL comment '用户的特征值',
PRIMARY KEY (`id`)
) ENGINE = InnoDB comment '用户特征表';
insert into user(username, phone) values ('nico', '18844445555');
insert into user_feature(user_id, feature_value) values (1, 'abc');
insert into user_feature(user_id, feature_value) values (1, 'def');
insert into user_feature(user_id, feature_value) values (1, 'ghi');
2.1 mock对象与spy对象
|--------|----------|--------------|----------|-----------|
| | 方法插桩 | 方法不插桩 | 作用对象 | 最佳实践 |
| mock对象 | 执行插桩逻辑 | 返回mock对象的默认值 | 类、接口 | 被测试类或其他依赖 |
| spy对象 | 执行插桩逻辑 | 调用真实方法 | 类、接口 | 被测试类 |
2.2 初始化mock/spy对象的方式★

spy对象是另一种不同类型的mock对象;mock对象不是spy对象
2.3 参数匹配
2.3.1 测试代码
java
package com.nico.mockito;
import com.nico.mockito.bean.req.UserUpdateReq;
import com.nico.mockito.bean.vo.UserVO;
import com.nico.mockito.service.UserService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
/**
* 参数匹配:通过方法签名(参数)来指定哪些方法调用需要被处理(插桩、verify验证)
* @auther Nico
* @since 2026/01/14 0014 19:31
*/
@ExtendWith(MockitoExtension.class)
public class ParamMatcherTest {
@Mock
private UserService mockUserService;
@Test
public void test4(){
List<String> features = new ArrayList<>();
mockUserService.add("Zelda", "111111", features);
// 校验参数为 "Zelda", "111111", features 的add方法调用了1次
Mockito.verify(mockUserService, Mockito.times(1)).add("Zelda", "111111", features);
// 报错When using matchers, all arguments have to be provided by matchers.
// Mockito.verify(mockUserService, Mockito.times(1)).add(ArgumentMatchers.anyString(), "111111", features);
Mockito.verify(mockUserService, Mockito.times(1)).add(anyString(), anyString(), anyList());
}
/**
* 拦截UserUpdateReq类型的任意实例对象
* ArgumentMatchers.any拦截UserUpdateReq类型的任意对象
* 除了any,还有anyXx(anyLong,anyString。。。),注意:它们都不包括null
*/
@Test
public void test3(){
// 指定参数为UserUpdateReq类的任意实例对象时,调用mockUserService.modifyById方法,返回99
Mockito.doReturn(99).when(mockUserService).modifyById(ArgumentMatchers.any(UserUpdateReq.class));
UserUpdateReq userUpdateReq1 = new UserUpdateReq();
userUpdateReq1.setId(1L);
userUpdateReq1.setPhone("1L");
int result1 = mockUserService.modifyById(userUpdateReq1);
int result3 = mockUserService.modifyById(userUpdateReq1);
System.out.println("result1 = " + result1);// result1 = 99
// =======================================================================================
UserUpdateReq userUpdateReq2 = new UserUpdateReq();
userUpdateReq2.setId(2L);
userUpdateReq2.setPhone("2L");
int result2 = mockUserService.modifyById(userUpdateReq2);
System.out.println("result2 = " + result2);// result2 = 99
}
/**
* 测试插桩时的参数匹配,只拦截userUpdateReq1
*/
@Test
public void test2(){
UserUpdateReq userUpdateReq1 = new UserUpdateReq();
userUpdateReq1.setId(1L);
userUpdateReq1.setPhone("1L");
// 指定参数为userUpdateReq1时,调用mockUserService.modifyById返回99
Mockito.doReturn(99).when(mockUserService).modifyById(userUpdateReq1);
int result1 = mockUserService.modifyById(userUpdateReq1);
int result3 = mockUserService.modifyById(userUpdateReq1);
System.out.println("result1 = " + result1);// result1 = 99
System.out.println("result3 = " + result3);// result3 = 99
// =======================================================================================
UserUpdateReq userUpdateReq2 = new UserUpdateReq();
userUpdateReq2.setId(2L);
userUpdateReq2.setPhone("2L");
int result2 = mockUserService.modifyById(userUpdateReq2);
System.out.println("result2 = " + result2);// result2 = 0
}
/**
* 对于mock对象不会调用真实方法,直接返回mock对象的默认值
* 默认值(int)、null(UserVO)、空集合(List)
*/
@Test
public void test1() {
UserVO userVO = mockUserService.selectById(1L);
System.out.println("userVO = " + userVO);// userVO = null
UserUpdateReq userUpdateReq1 = new UserUpdateReq();
int i = mockUserService.modifyById(userUpdateReq1);
System.out.println("i = " + i);// i = 0
}
}
2.4 方法插桩
指定调用某个方法时的行为(stubbing),达到相互隔离的目的
返回指定值
void返回值方法插桩
插桩的两种方式:
when(obj.someMethod()).thenXxx():
其中obj可以是mock对象。spy对象在没有插桩时是调用真实方法的,写在when中会导致先执行一次原方法,达不到mock的目的。因此spy一般使用下面的方法
doXxx().when(obj).someMethod():
其中obj可以是mock/spy对象,或对无返回值的方法进行插桩
抛异常
多次插桩
thenAnswer
执行真正的原始方法
verify的使用
2.4.1 指定返回值
java
package com.nico.mockito;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
/**
* @auther Nico
* @since 2026/01/15 0015 13:21
*/
@ExtendWith(MockitoExtension.class)
public class StubTest {
@Mock
private List<String> mockList;
/**
* 指定返回值
*/
@Test
public void test1(){
// 方法一
doReturn("zero").when(mockList).get(0);
// Junit5里面自带的断言:如果返回值不相等,则本单元测试会失败
Assertions.assertEquals("zero", mockList.get(0));
// 方法二
when(mockList.get(1)).thenReturn("one");
Assertions.assertEquals("one", mockList.get(1));
}
}
2.4.2 void返回值方法插桩
java
@Mock
private List<String> mockList;
/**
* void返回值方法插桩
*/
@Test
public void test2(){
// 调用mockList.clear的时候什么也不做
doNothing().when(mockList).clear();
mockList.clear();
// 验证调用了一次clear
verify(mockList, times(1)).clear();
}
2.4.3 插桩的两种方式
java
@Mock
private UserServiceImpl mockUserServiceImpl;
@Spy
private UserServiceImpl spyUserServiceImpl;
/**
* 插桩的两种方式
*/
@Test
public void test3(){
when(mockUserServiceImpl.getNumber()).thenReturn(99);
System.out.println("mockUserServiceImpl.getNumber() = " + mockUserServiceImpl.getNumber());// mockUserServiceImpl.getNumber() = 99
when(spyUserServiceImpl.getNumber()).thenReturn(99);// 这里的spy对象,就会先执行真实方法一次
/**
* getNumber
* spyUserServiceImpl.getNumber() = 99
* spy对象在没有插桩时是调用真实方法的,写在when中会导致先执行一次原方法,达不到mock的目的,
* 需要使用doXxx().when(obj).someMethod()
*/
System.out.println("spyUserServiceImpl.getNumber() = " + spyUserServiceImpl.getNumber());
doReturn(1000).when(spyUserServiceImpl).getNumber();
System.out.println("spyUserServiceImpl.getNumber() = " + spyUserServiceImpl.getNumber());// spyUserServiceImpl.getNumber() = 1000
}
2.4.4 抛出异常
java
@Mock
private List<String> mockList;
/**
* 抛出异常
*/
@Test
public void test4() {
// 方法一
doThrow(RuntimeException.class).when(mockList).clear();
try {
mockList.clear();
// 走到下面这行说明插桩失败了
Assertions.fail();
} catch (Exception e) {
// 断言表达式为真
Assertions.assertTrue(e instanceof RuntimeException);
}
// 方法二
when(mockList.get(anyInt())).thenThrow(RuntimeException.class);
try {
mockList.get(4);
Assertions.fail();
} catch (Exception e) {
Assertions.assertTrue(e instanceof RuntimeException);
}
}
2.4.5 多次插桩
java
@Mock
private List<String> mockList;
/**
* 多次插桩
*/
@Test
public void test5() {
// 第一次调用返回1,第二次调用返回2,第三次及之后的调用都返回3
// when(mockList.size()).thenReturn(1).thenReturn(2).thenReturn(3);
// 可简写为:
when(mockList.size()).thenReturn(1, 2, 3);
Assertions.assertEquals(1, mockList.size());
Assertions.assertEquals(2, mockList.size());
Assertions.assertEquals(3, mockList.size());
Assertions.assertEquals(3, mockList.size());
}
2.4.6 thenAnswer指定插桩逻辑
java
@Mock
private List<String> mockList;
/**
* thenAnswer 实现指定逻辑的插桩
*/
@Test
public void test6(){
when(mockList.get(anyInt())).thenAnswer(new Answer<String>() {
/**
* 泛型表示要插桩方法的返回值类型
*/
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
// getArgument表示获取插桩方法(此处就是List.get)的第几个参数值
Integer argument = invocation.getArgument(0, Integer.class);
return String.valueOf(argument * 100);
}
});
String result = mockList.get(3);
Assertions.assertEquals("300", result);
}
2.4.7 执行真正的原始方法
java
@Mock
private UserServiceImpl mockUserServiceImpl;
@Spy
private UserServiceImpl spyUserServiceImpl;
/**
* 执行真正的原始方法
*/
@Test
public void test7() {
// 对mock对象插桩,让它执行原始方法
when(mockUserServiceImpl.getNumber()).thenCallRealMethod();
int number = mockUserServiceImpl.getNumber();
Assertions.assertEquals(0, number);
// spy对象默认就会调用真实方法,如果不想让它调用,需要单独为它进行插桩
int spyResult = spyUserServiceImpl.getNumber();
Assertions.assertEquals(0, spyResult);
// 不让spy对象调用真实方法
doReturn(1000).when(spyUserServiceImpl).getNumber();
spyResult = spyUserServiceImpl.getNumber();
Assertions.assertEquals(1000, spyResult);
}
2.4.8 verify的使用
java
@Mock
private List<String> mockList;
/**
* 测试verify
*/
@Test
public void test8() {
mockList.add("one");
Assertions.assertEquals(0, mockList.size());// true:调用mock对象的写操作方法是没效果的
mockList.clear();
// 验证调用过1次add方法,且参数必须是one
verify(mockList).add("one");// 指定要验证的方法和参数,这里不是调用,也不会产生调用效果
verify(mockList, times(1)).clear();// 等价于上面verify(mockedList)
// 校验没有调用的两种方式
verify(mockList, times(0)).get(1);
verify(mockList, never()).get(1);
// 校验最少或最多调用了多少次
verify(mockList, atLeast(1)).clear();
verify(mockList, atMost(1)).clear();
}
2.5 @InjectMocks注解的使用
作用:若@InjectMocks声明的变量需要用到mock/spy对象,mockito会自动使用当前类里的mock或spy成员进行按类型或名字的注入
原理:构造器注入、setter注入、字段反射注入(按构造器、setter、字段反射的顺序判断选择哪种注入,如果构造器中有参数,则优先用构造器注入,后面依次类推)
spy对象是另一种不同类型的mock对象;mock对象不是spy对象
@InjectMocks 一般和 @Spy搭配使用,如果只有@InjectMocks的话,注入的就是一个普通的实例对象,一个未经过Mockito处理的普通对象

如果加上@Spy后,注入的就是一个经过Mockito处理的对象

java
package com.nico.mockito;
import com.nico.mockito.service.UserFeatureService;
import com.nico.mockito.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
/**
* @auther Nico
* @since 2026/01/15 0015 22:59
*/
@ExtendWith(MockitoExtension.class)
public class InjectMocksTest {
/**
* 1.被@InjectMocks标注的属性必须是实现类,因为mockito会创建对应的实例对象,默认创建的对象就是
* 未经过mockito处理的普通对象,因此常配合@Spy注解使其变为默认调用真实方法的mock对象
* 2.mockito会使用spy对象或mock对象 注入到被@InjectMocks标注的实例对象中(如在userService中注入userFeatureService属性)
*/
@InjectMocks
@Spy
private UserServiceImpl userService;
@Mock
private UserFeatureService userFeatureService;//UserServiceImpl中有UserFeatureService属性,就会注入
@Mock
private List<String> mockList;//UserServiceImpl中没有List<String>属性,就不会注入
@Test
public void test1() {
int number = userService.getNumber();
Assertions.assertEquals(0, number);
}
}
2.6 断言工具
hamcrest:junit4中引入的第三方断言库,junit5中被移除,从1.3版本后,坐标由org.hamcrest:hamcrest变为org.hamcrest:hamcrest(比较老了,用得少)
AssertJ:常用的断言库
Junit4 原生断言
Junit5 原生断言
java
package com.nico.mockito;
import org.assertj.core.api.Assertions;
import org.hamcrest.MatcherAssert;
import org.hamcrest.core.IsEqual;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import static org.mockito.Mockito.when;
/**
* @auther Nico
* @since 2026/01/16 0016 19:51
*/
@ExtendWith(MockitoExtension.class)
public class AssertTest {
@Mock
private List<String> mockList;
@Test
public void test1() {
when(mockList.size()).thenReturn(999);
/**
* 测试hamcrest的断言
*/
MatcherAssert.assertThat(mockList.size(), IsEqual.equalTo(999));
/**
* 测试 AssertJ
* assertThat:参数为实际的值
*/
Assertions.assertThat(mockList.size()).isEqualTo(999);
/**
* Junit5原生断言
*/
org.junit.jupiter.api.Assertions.assertEquals(999, mockList.size());
/**
* Junit4原生断言
*/
// org.junit.Assert.assertEquals(999, mockList.size());
}
}
2.8 Assertions(断言)和verify(验证)的区别
核心区别
| 方面 | Assertions(断言) | verify(验证) |
|---|---|---|
| 关注点 | 验证业务结果是否正确 | 验证方法调用是否正确 |
| 测试对象 | 业务数据、返回值、状态 | Mock对象的交互行为 |
| 使用场景 | 所有测试(单元、集成) | Mockito测试(主要是单元测试) |
| 底层原理 | 断言库(JUnit、AssertJ) | Mockito框架的调用追踪 |
总结
-
✅ Assertions :验证业务逻辑的正确性(结果对不对)
-
✅ verify :验证代码的交互行为(方法调没调用、调了几次)
-
✅ 两者互补:好的单元测试应该同时包含两者
简单记忆:
-
Assertions 回答:"结果正确吗?"
-
verify 回答:"方法调用正确吗?"
在系统测试中,应该同时使用两者来确保代码的正确性。
3 实战讲解
java
package com.nico.mockito;
import com.nico.mockito.bean.entity.UserDO;
import com.nico.mockito.bean.entity.UserFeatureDO;
import com.nico.mockito.bean.vo.UserVO;
import com.nico.mockito.mapper.UserFeatureMapper;
import com.nico.mockito.mapper.UserMapper;
import com.nico.mockito.service.UserFeatureService;
import com.nico.mockito.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.Mockito.doReturn;
/**
* 实战讲解
* @auther Nico
* @since 2026/01/16 0016 20:54
*/
@ExtendWith(MockitoExtension.class)
public class UserServiceImplTest {
@InjectMocks
@Spy
private UserServiceImpl userService;
@Mock
private UserFeatureService userFeatureService;
@Mock
private UserMapper userMapper;
@Test
public void testSelectById3() {
// 配置方法getById的返回值
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setUsername("Nico");
userDO.setPhone("13344445555");
doReturn(userDO).when(userService).getById(1L);
// 配置userFeatureService.selectByUserId的返回值
List<UserFeatureDO> userFeatureDOList = new ArrayList<>();
UserFeatureDO userFeatureDO = new UserFeatureDO();
userFeatureDO.setId(88L);
userFeatureDO.setUserId(1L);
userFeatureDO.setFeatureValue("aaaa");
userFeatureDOList.add(userFeatureDO);
doReturn(userFeatureDOList).when(userFeatureService).selectByUserId(1L);
// 执行测试
UserVO userVO = userService.selectById(1L);
// 断言
Assertions.assertEquals(1, userVO.getFeatureValue().size());
}
@Test
public void testSelectById2() {
// 配置方法getById的返回值
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setUsername("Nico");
userDO.setPhone("13344445555");
doReturn(userDO).when(userService).getById(1L);
// 执行测试
UserVO userVO = userService.selectById(1L);
// 断言
Assertions.assertNotNull(userVO);
}
@Test
public void testSelectById1() {
// 配置
doReturn(userMapper).when(userService).getBaseMapper();
UserVO userVO = userService.selectById(1L);
Assertions.assertNull(userVO);
}
}
4 Mockito在SpringBoot环境使用(不推荐)
生成的对象受Spring管理,相当于自动替换对应类型bean的注入
4.1 @MockBean
类似@Mock
用于通过类型或名字替换Spring容器中已经存在的bean,从而达到对这些bean进行mock的目的
4.2 @SpyBean
作用类似于@Spy
用于通过类型或名字包装Spring容器中已经存在的bean,当需要mock被测试类的某些方法时可以使用
java
package com.nico.mockito.spring;
import com.nico.mockito.MockitoDemoApplication;
import com.nico.mockito.bean.entity.UserDO;
import com.nico.mockito.bean.entity.UserFeatureDO;
import com.nico.mockito.bean.vo.UserVO;
import com.nico.mockito.mapper.UserMapper;
import com.nico.mockito.service.UserFeatureService;
import com.nico.mockito.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Spy;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.Mockito.doReturn;
/**
* mockito配合spring使用
* @auther Nico
* @since 2026/01/16 0016 20:54
*/
@SpringBootTest(classes = MockitoDemoApplication.class)
public class UserServiceImplInSpringTest {
/**
* 不能配置@Spy:Argument passed to when() is not a mock
*/
// @Resource
@SpyBean
private UserServiceImpl userService;
@MockBean
private UserFeatureService userFeatureService;
@MockBean
private UserMapper userMapper;
@SpyBean
private DataSourceProperties dataSourceProperties;
@Test
public void testSelectById3() {
System.out.println(dataSourceProperties);
// 配置方法getById的返回值
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setUsername("Nico");
userDO.setPhone("13344445555");
doReturn(userDO).when(userService).getById(1L);
// 配置userFeatureService.selectByUserId的返回值
List<UserFeatureDO> userFeatureDOList = new ArrayList<>();
UserFeatureDO userFeatureDO = new UserFeatureDO();
userFeatureDO.setId(88L);
userFeatureDO.setUserId(1L);
userFeatureDO.setFeatureValue("aaaa");
userFeatureDOList.add(userFeatureDO);
doReturn(userFeatureDOList).when(userFeatureService).selectByUserId(1L);
// 执行测试
UserVO userVO = userService.selectById(1L);
// 断言
Assertions.assertEquals(1, userVO.getFeatureValue().size());
}
@Test
public void testSelectById2() {
// 配置方法getById的返回值
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setUsername("Nico");
userDO.setPhone("13344445555");
doReturn(userDO).when(userService).getById(1L);
// 执行测试
UserVO userVO = userService.selectById(1L);
// 断言
Assertions.assertNotNull(userVO);
}
@Test
public void testSelectById1() {
// 配置
UserVO userVO = userService.selectById(1L);
Assertions.assertNull(userVO);
}
}
5 使用插件Squaretest快速编写单元测试
idea 插件:Squaretest
插件设置:




java
package com.nico.mockito.service.impl;
import com.nico.mockito.bean.entity.UserDO;
import com.nico.mockito.bean.entity.UserFeatureDO;
import com.nico.mockito.bean.req.UserUpdateReq;
import com.nico.mockito.bean.vo.UserVO;
import com.nico.mockito.service.UserFeatureService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
/**
* squaretest使用:
* 1.先运行一下,根据报错进行修改
* 2.被测试类一般要加上@Spy(因为可能需要对某些方法进行插桩)
* 3.依次检查各个方法,根据错误提示进行修改(对一些方法进行插桩、修改返回值、修改断言......)
*/
@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {
@Mock
private UserFeatureService mockUserFeatureService;
@InjectMocks
@Spy //这里不能是@Mock,必须是@Spy @Mock对象所有方法都是假的。
// 这里我们测试UserServiceImpl,调用真实方法,方法中的方法进行插桩,定制返回值
private UserServiceImpl userServiceImplUnderTest;
@Test
void testSelectById() {
// Setup
// Configure UserFeatureService.getById(...).
UserDO existedEntity = new UserDO();
existedEntity.setId(0L);
existedEntity.setUsername("username");
existedEntity.setPhone("phone");
doReturn(existedEntity).when(userServiceImplUnderTest).getById(0L);
// Configure UserFeatureService.selectByUserId(...).
final UserFeatureDO userFeatureDO = new UserFeatureDO();
userFeatureDO.setId(0L);
userFeatureDO.setUserId(0L);
userFeatureDO.setFeatureValue("featureValue");
final List<UserFeatureDO> userFeatureDOList = Arrays.asList(userFeatureDO);
when(mockUserFeatureService.selectByUserId(0L)).thenReturn(userFeatureDOList);
// Run the test
final UserVO result = userServiceImplUnderTest.selectById(0L);
// Verify the results
assertThat(result.getFeatureValue().size()).isEqualTo(1);
}
@Test
void testSelectById_UserFeatureServiceReturnsNoItems() {
// Setup
// Configure UserFeatureService.getById(...).
UserDO existedEntity = new UserDO();
existedEntity.setId(0L);
existedEntity.setUsername("username");
existedEntity.setPhone("phone");
doReturn(existedEntity).when(userServiceImplUnderTest).getById(0L);
when(mockUserFeatureService.selectByUserId(0L)).thenReturn(Collections.emptyList());
// Run the test
final UserVO result = userServiceImplUnderTest.selectById(0L);
// Verify the results
assertThat(result.getFeatureValue()).isNull();
}
@Test
void testAdd() {
// Setup
when(mockUserFeatureService.saveBatch(anyList())).thenReturn(true);
// config
doReturn(true).when(userServiceImplUnderTest).save(any(UserDO.class));
// Run the test
// userServiceImplUnderTest.add("username", "phone", Arrays.asList("value"));
// 因为userServiceImplUnderTest.add方法是void返回值,所以可以断言它不抛异常
Assertions.assertDoesNotThrow(() -> userServiceImplUnderTest.add("username", "phone", Arrays.asList("value")));
// Verify the results
verify(mockUserFeatureService).saveBatch(anyList());
}
@Test
void testModifyById() {
// Setup
final UserUpdateReq userUpdateReq = new UserUpdateReq();
userUpdateReq.setId(0L);
userUpdateReq.setPhone("phone");
// config
doReturn(true).when(userServiceImplUnderTest).updateById(any(UserDO.class));
// Run the test
final int result = userServiceImplUnderTest.modifyById(userUpdateReq);
// Verify the results
assertThat(result).isEqualTo(1);
}
@Test
void testGetNumber() {
assertThat(userServiceImplUnderTest.getNumber()).isEqualTo(0);
}
}