当前位置: 代码网 > it编程>编程语言>Java > SpringBoot整合Mockito进行单元测试实践

SpringBoot整合Mockito进行单元测试实践

2026年05月10日 Java 我要评论
一、环境准备1. 添加 maven 依赖<dependencies><!-- spring boot 测试核心依赖 --><dependency><group

一、环境准备

1. 添加 maven 依赖

<dependencies>
<!-- spring boot 测试核心依赖 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>
<!-- mockito 核心库 -->
<dependency>
<groupid>org.mockito</groupid>
<artifactid>mockito-core</artifactid>
<scope>test</scope>
</dependency>
<!-- 用于断言的实用工具 -->
<dependency>
<groupid>org.assertj</groupid>
<artifactid>assertj-core</artifactid>
<scope>test</scope>
</dependency>
<!-- json 路径验证(用于 controller 测试) -->
<dependency>
<groupid>com.jayway.jsonpath</groupid>
<artifactid>json-path-assert</artifactid>
<scope>test</scope>
</dependency>
</dependencies>

二、核心概念与工具

1.junit 5 注解

注解用途
@test标记测试方法
@beforeeach每个测试方法执行前调用
@aftereach每个测试方法执行后调用
@displayname为测试方法设置可读名称
@parameterizedtest支持参数化测试(多组输入测试同一逻辑)

2.mockito 注解

注解用途
@mock创建模拟对象(非 spring 容器管理)
@injectmocks自动注入被测试类(配合 @mock 使用)
@mockbean在 spring 容器中创建模拟 bean(适用于 @springboottest)
@spy创建部分模拟对象(真实方法调用 + 指定方法模拟)

三、实战案例详解

案例 1:纯 java 服务类测试(无 spring 上下文)

1. 被测试类

public class userservice {
private final userrepository userrepository;

public userservice(userrepository userrepository) {
this.userrepository = userrepository;
}

public user getuserbyid(long id) {
return userrepository.findbyid(id).orelse(null);
}
}

2. 单元测试

import static org.mockito.mockito.*;
import static org.junit.jupiter.api.assertions.*;
import static org.assertj.core.api.assertions.assertthat;

import org.junit.jupiter.api.beforeeach;
import org.junit.jupiter.api.test;
import org.mockito.injectmocks;
import org.mockito.mock;
import org.mockito.mockitoannotations;

public class userservicetest {

@mock
private userrepository userrepository;

@injectmocks
private userservice userservice;

@beforeeach
void setup() {
mockitoannotations.openmocks(this);
}

@test
@displayname("测试获取用户 - 用户存在")
void testgetuserbyid_userexists() {
// 1. 模拟 repository 行为
user mockuser = new user(1l, "alice", "alice@example.com");
when(userrepository.findbyid(1l)).thenreturn(optional.of(mockuser));

// 2. 执行测试方法
user result = userservice.getuserbyid(1l);

// 3. 验证结果
assertnotnull(result);
assertequals("alice", result.getname());
assertequals("alice@example.com", result.getemail());

// 4. 验证方法调用
verify(userrepository, times(1)).findbyid(1l);
}

@test
@displayname("测试获取用户 - 用户不存在")
void testgetuserbyid_usernotexists() {
// 1. 模拟 repository 行为
when(userrepository.findbyid(999l)).thenreturn(optional.empty());

// 2. 执行测试方法
user result = userservice.getuserbyid(999l);

// 3. 验证结果
assertnull(result);
verify(userrepository, times(1)).findbyid(999l);
}
}

案例 2:spring boot controller 层测试(使用 mockmvc)

1. 被测试类

@restcontroller
@requestmapping("/users")
public class usercontroller {
@autowired
private userservice userservice;

@getmapping("/{id}")
public responseentity<user> getuser(@pathvariable long id) {
user user = userservice.getuserbyid(id);
return responseentity.ok(user);
}
}

2. 单元测试

import static org.springframework.test.web.servlet.request.mockmvcrequestbuilders.*;
import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.*;
import static org.springframework.test.web.servlet.result.mockmvcresulthandlers.*;

import org.junit.jupiter.api.test;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.webmvctest;
import org.springframework.boot.test.mock.mockito.mockbean;
import org.springframework.test.web.servlet.mockmvc;

@webmvctest(usercontroller.class)
public class usercontrollertest {

@autowired
private mockmvc mockmvc;

@mockbean
private userservice userservice;

@test
@displayname("测试获取用户 - 用户存在")
void testgetuserbyid_userexists() throws exception {
// 1. 模拟 service 返回值
user mockuser = new user(1l, "alice", "alice@example.com");
when(userservice.getuserbyid(1l)).thenreturn(mockuser);

// 2. 发起 http 请求
mockmvc.perform(get("/users/1"))
.andexpect(status().isok())
.andexpect(jsonpath("$.id").value(1l))
.andexpect(jsonpath("$.name").value("alice"))
.andexpect(jsonpath("$.email").value("alice@example.com"))
.anddo(print());
}

@test
@displayname("测试获取用户 - 用户不存在")
void testgetuserbyid_usernotexists() throws exception {
// 1. 模拟 service 返回值
when(userservice.getuserbyid(999l)).thenreturn(null);

// 2. 发起 http 请求
mockmvc.perform(get("/users/999"))
.andexpect(status().isnotfound());
}
}

案例 3:repository 层测试(使用内存数据库 h2)

1. 配置 h2 内存数据库

@datajpatest
public class userrepositorytest {

@autowired
private userrepository userrepository;

@test
@displayname("测试保存用户")
void testsaveuser() {
// 1. 准备测试数据
user user = new user(null, "bob", "bob@example.com");
user saveduser = userrepository.save(user);

// 2. 验证结果
assertnotnull(saveduser.getid());
assertequals("bob", saveduser.getname());
assertequals("bob@example.com", saveduser.getemail());

// 3. 验证数据库记录
user founduser = userrepository.findbyid(saveduser.getid()).orelse(null);
assertnotnull(founduser);
assertequals("bob", founduser.getname());
}

@test
@displayname("测试查询用户")
void testfindbyemail() {
// 1. 准备测试数据
user user = new user(null, "charlie", "charlie@example.com");
userrepository.save(user);

// 2. 查询验证
user result = userrepository.findbyemail("charlie@example.com");
assertnotnull(result);
assertequals("charlie", result.getname());
assertequals("charlie@example.com", result.getemail());
}
}

案例 4:service 层测试(结合异常场景)

1. 被测试类

@service
public class userservice {
@autowired
private userrepository userrepository;

public user getuserbyid(long id) {
return userrepository.findbyid(id).orelsethrow(() -> new usernotfoundexception("user not found"));
}
}

2. 单元测试

import static org.mockito.mockito.*;
import static org.junit.jupiter.api.assertions.*;

public class userservicetest {

@mock
private userrepository userrepository;

@injectmocks
private userservice userservice;

@beforeeach
void setup() {
mockitoannotations.openmocks(this);
}

@test
@displayname("测试获取用户 - 抛出异常")
void testgetuserbyid_throwsexception() {
// 1. 模拟 repository 行为
when(userrepository.findbyid(999l)).thenreturn(optional.empty());

// 2. 执行测试并捕获异常
assertthrows(usernotfoundexception.class, () -> {
userservice.getuserbyid(999l);
});

// 3. 验证方法调用
verify(userrepository, times(1)).findbyid(999l);
}
}

四、mockito 高级用法

1.模拟任意参数

when(userservice.getuserbyid(anylong())).thenreturn(new user(1l, "test user"));

2.参数捕获与验证

argumentcaptor<user> usercaptor = argumentcaptor.forclass(user.class);
verify(userrepository).save(usercaptor.capture());
user captureduser = usercaptor.getvalue();
assertequals("alice", captureduser.getname());

3.部分模拟(spy)

@spy
private userservice userservice;

@test
void testspy() {
doreturn(new user(1l, "mock user")).when(userservice).getuserbyid(anylong());
user result = userservice.getuserbyid(1l);
assertequals("mock user", result.getname());
}

4.bdd 风格测试

@test
void testbddstyle() {
// given
user mockuser = new user(1l, "alice");
when(userservice.getuserbyid(1l)).thenreturn(mockuser);

// when
user result = userservice.getuserbyid(1l);

// then
assertnotnull(result);
assertequals("alice", result.getname());
}

五、测试覆盖率与报告

1.jacoco 覆盖率报告

1.1 添加依赖

<dependency>
<groupid>org.jacoco</groupid>
<artifactid>jacoco-maven-plugin</artifactid>
<version>0.8.11</version>
</dependency>

1.2 执行测试并生成报告

mvn clean test
mvn jacoco:report

1.3 查看报告

报告路径:target/site/jacoco/index.html

六、常见问题与解决方案

1.问题:@mock注解未生效

  • 原因:未调用 mockitoannotations.openmocks(this)
  • 解决方案:在 @beforeeach 方法中初始化

2.问题:spring 上下文未加载

  • 原因:未使用 @springboottest
  • 解决方案:使用 @springboottest 或更细粒度的测试注解(如 @webmvctest

3.问题:测试方法执行顺序异常

  • 原因:junit 5 默认按字母顺序执行测试方法
  • 解决方案:使用 @order 注解或 @testmethodorder

4.问题:测试数据库污染

  • 原因:测试数据未清理
  • 解决方案:使用 @dirtiescontext 或手动清理数据

七、总结

测试类型推荐注解适用场景
纯 java 服务类@mock + @injectmocks无 spring 上下文的单元测试
spring controller@webmvctest + @mockbeanhttp 接口测试
repository 层@datajpatest数据库操作验证
集成测试@springboottest多组件协作验证

八、扩展学习

  1. 测试框架对比:junit 5 vs testng
  2. 测试覆盖率工具:jacoco、clover
  3. 测试驱动开发(tdd):先写测试再写代码
  4. 持续集成(ci):github actions + test coverage

测试原则

  • 单元测试:关注单一职责,快速失败,无需外部依赖
  • 集成测试:验证组件协作,模拟真实环境
  • 测试覆盖率:追求 80%+ 核心逻辑覆盖,避免过度测试

测试目标

  • 所有边界条件都被覆盖
  • 所有异常场景都被模拟
  • 所有业务逻辑都有验证

通过本教程,您应能全面掌握 spring boot 与 mockito 的单元测试技巧,并能够编写高效、可靠的测试代码。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com