spring boot 提供了完整的测试支持,核心是 spring-boot-starter-test,它整合了 junit、mockito、assertj、hamcrest、jsonpath 等主流测试库。
一、依赖引入
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>这个依赖默认提供了以下测试框架:
- junit 5:测试运行引擎
- mockito:mock 框架
- assertj:流式断言
- hamcrest:匹配器
- jsonpath:json 断言
- spring test:spring 测试支持
二、测试注解
@springboottest
加载完整的 spring 应用上下文,用于集成测试。默认情况下,它会查找 @springbootapplication 启动类并加载所有配置。
@springboottest
public class userservicetest {
@autowired
private userservice userservice;
@test
public void testgetuser() {
user user = userservice.getbyid(1l);
assertthat(user).isnotnull();
}
}
@springboottest 支持通过 webenvironment 属性控制 web 环境:
| webenvironment 值 | 说明 |
|---|---|
mock | 默认值,加载模拟的 servlet 环境,不启动真实 web 服务器 |
random_port | 启动内嵌 web 服务器,使用随机端口 |
defined_port | 启动内嵌 web 服务器,使用配置的端口 |
none | 不加载 web 环境,适用于非 web 测试 |
@test
junit 5 的测试方法注解,标记一个方法为测试用例。
@test
public void testadduser() {
// 测试逻辑
}
三、web 层切片测试
@webmvctest
只加载 web 层组件(controller、handlerinterceptor、webmvcconfigurer),不加载 service、mapper 等业务组件。测试 controller 层性能更好,启动更快。
@webmvctest(usercontroller.class)
public class usercontrollertest {
@autowired
private mockmvc mockmvc;
@mockbean
private userservice userservice;
@test
public void testgetuser() throws exception {
user mockuser = new user(1l, "张三");
when(userservice.getbyid(1l)).thenreturn(mockuser);
mockmvc.perform(get("/user/1"))
.andexpect(status().isok())
.andexpect(jsonpath("$.name").value("张三"));
}
}
@webmvctest 只加载指定的 controller,service 层需要配合 @mockbean 模拟依赖。如果 controller 依赖其他组件(如拦截器、参数解析器),需要额外配置或使用 @import 导入。
mockmvc
mockmvc 是 spring 提供的模拟 http 请求的工具,无需启动真实 web 服务器就能测试 controller 的行为。
常用方法:
mockmvc.perform(get("/user/1"))
.andexpect(status().isok())
.andexpect(jsonpath("$.name").value("张三"))
.andexpect(jsonpath("$.age").value(18))
.anddo(print());
四、数据访问层切片测试
@datajpatest
只加载 jpa 相关组件(repository、entitymanager、datasource),默认使用嵌入式内存数据库(h2 或 derby),测试数据不会污染真实数据库。
@datajpatest
public class userrepositorytest {
@autowired
private userrepository userrepository;
@test
public void testsaveuser() {
user user = new user("张三");
user saved = userrepository.save(user);
assertthat(saved.getid()).isnotnull();
}
}
@mybatistest
mybatis 生态的切片测试注解,只加载 mybatis 相关组件(mapper、sqlsessionfactory)。同样默认使用内存数据库。
@mybatistest
public class usermappertest {
@autowired
private usermapper usermapper;
@test
public void testselectbyid() {
user user = usermapper.selectbyid(1l);
assertthat(user).isnotnull();
}
}
五、mock 相关注解
@mockbean
在 spring 容器中创建一个 mockito mock 对象,并替换同类型的 bean。常用于隔离外部依赖。
@springboottest
public class userservicetest {
@mockbean
private usermapper usermapper;
@autowired
private userservice userservice;
@test
public void testgetuser() {
user mockuser = new user(1l, "张三");
when(usermapper.selectbyid(1l)).thenreturn(mockuser);
user result = userservice.getbyid(1l);
assertthat(result.getname()).isequalto("张三");
}
}
@spybean
创建 mockito spy 对象,保留真实对象的部分行为,可以部分 mock。
@spybean
private userservice userservice;
@test
public void testpartialmock() {
// 真实调用会执行真实逻辑,只有 doreturn 的部分被 mock
doreturn("mocked name").when(userservice).getname();
}
@mockbean 和 @spybean 的区别在于:@mockbean 完全模拟对象,所有方法调用都返回默认值或需要显式 stubbing;@spybean 保留真实对象的行为,只对显式 stubbing 的方法返回 mock 值。
六、配置覆盖
@testpropertysource
在测试类上指定额外的属性配置,覆盖主配置文件中的属性。
@springboottest
@testpropertysource(properties = {
"app.name=test-app",
"app.timeout=5000"
})
public class configtest {
@value("${app.name}")
private string appname;
@test
public void testproperties() {
assertthat(appname).isequalto("test-app");
}
}
@testconfiguration
在测试类中定义额外的配置,仅在测试环境中生效,不会影响主应用。
@testconfiguration
public class testconfig {
@bean
public userservice mockuserservice() {
return mock(userservice.class);
}
}
@springboottest
@import(testconfig.class)
public class servicetest {
@autowired
private userservice userservice;
}
七、事务与回滚
默认情况下,@datajpatest 和 @mybatistest 会在每个测试方法结束后自动回滚事务,测试数据不会持久化到数据库。
@springboottest 默认不自动回滚,需要手动添加 @transactional 和 @rollback:
@springboottest
@transactional
@rollback
public class userservicetest {
@test
public void testinsertuser() {
userservice.insert(new user("张三"));
// 测试结束后自动回滚
}
}
八、断言工具
assertj
spring-boot-starter-test 默认依赖 assertj,提供了流式断言 api:
// 基本断言
assertthat(user.getname()).isequalto("张三");
assertthat(user.getage()).isgreaterthan(18);
// 集合断言
assertthat(userlist).hassize(3);
assertthat(userlist).extracting(user::getname).contains("张三", "李四");
// 异常断言
assertthatthrownby(() -> userservice.getbyid(null))
.isinstanceof(illegalargumentexception.class)
.hasmessagecontaining("id不能为空");
jsonpath
用于断言 json 响应:
mockmvc.perform(get("/user/1"))
.andexpect(jsonpath("$.id").value(1))
.andexpect(jsonpath("$.name").value("张三"))
.andexpect(jsonpath("$.orders.length()").value(3));
九、上下文缓存
spring 测试框架会缓存 applicationcontext。如果多个测试类有相同的配置,它们会复用同一个上下文,避免重复加载,加快测试执行速度。
// test1 加载了上下文
@springboottest
class test1 { }
// test2 复用 test1 的上下文(配置相同)
@springboottest
class test2 { }
如果某个测试类使用了不同的配置(如 @mockbean 或 @testpropertysource),会创建新的上下文,缓存失效。
// 上下文 a:使用 @mockbean
@springboottest
class testwithmock { }
// 上下文 b:没有 @mockbean(不同配置,不能复用)
@springboottest
class testwithoutmock { }
十、最佳实践
- 使用切片测试(
@webmvctest、@datajpatest)代替完整@springboottest,启动速度更快,隔离性更好 - 单元测试用
@mockbean模拟外部依赖,减少测试对环境的依赖 - 在
@datajpatest和@mybatistest中,对查询逻辑进行验证,确保 sql 正确性 @webmvctest测试 controller 时验证 http 状态码、响应体结构、json 字段类型,确保接口契约正确- 用
@testpropertysource覆盖敏感配置(如第三方 api key、数据库连接串),避免测试代码泄露敏感信息 - 保持测试独立性,每个测试方法执行后恢复上下文状态,避免测试间依赖执行顺序
@beforeeach和@aftereach用于准备测试数据和清理资源- 测试方法命名清晰,如
shouldreturnuserwhenidexists比testgetuser更能表达意图 - 使用
@displayname为测试方法添加中文或可读性更强的描述 - 生产代码和测试代码保持相同包结构,方便访问包级私有成员
到此这篇关于springboot实现单元测试的完全指南的文章就介绍到这了,更多相关springboot单元测试内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论