@SpringBootTest 详解:Spring Boot 测试的核心注解
@SpringBootTest 是 Spring Boot 测试框架中最核心的注解,它的本质是启动完整的 Spring 应用上下文,让测试代码能在真实的 Spring 环境中运行。
一、它的作用是什么?
单元测试通常只测试一个类的行为,不涉及 Spring 容器的加载。但集成测试需要验证 Controller、Service、Mapper 之间的协作,这个时候就需要 Spring 容器来管理 Bean 之间的依赖关系。
@SpringBootTest 做的就是这件事:启动 Spring 容器,加载完整的 ApplicationContext,让 @Autowired 能正常注入 Bean,让配置文件生效,让 AOP 代理生效。
java
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testGetUser() {
User user = userService.getById(1L);
Assertions.assertNotNull(user);
}
}
二、核心属性:webEnvironment
webEnvironment 控制测试的 Web 环境类型,默认值是 MOCK。
| 取值 | 说明 |
|---|---|
MOCK |
默认值,加载模拟的 Servlet 环境,不启动真实 Web 服务器。配合 MockMvc 测试 Controller |
RANDOM_PORT |
启动内嵌 Web 服务器,使用随机端口。适用于需要真实 HTTP 请求的测试,如 RestTemplate 调用 |
DEFINED_PORT |
启动内嵌 Web 服务器,使用 application.yml 中配置的端口 |
NONE |
不加载 Web 环境,适用于不涉及 Web 层的测试,如纯 Service 层或 Mapper 层测试 |
java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {
@LocalServerPort
private int port;
@Test
public void testGetUser() {
// 通过真实端口调用
restTemplate.getForObject("http://localhost:" + port + "/user/1", User.class);
}
}
MOCK 和 RANDOM_PORT 的区别在于:MOCK 模拟 Servlet 环境,MockMvc 发起的请求不经过网络,直接由 DispatcherServlet 处理;RANDOM_PORT 启动真实 Tomcat,请求经过完整的网络栈。
三、@SpringBootTest 的加载机制
@SpringBootTest 在测试启动时,会根据当前类路径和配置推断应用类型(Servlet、Reactive 或非 Web),然后创建对应的 ApplicationContext。它会搜索 @SpringBootApplication 或 @SpringBootConfiguration 标注的类作为配置源,加载所有自动配置。
加载过程相当于执行了一次 SpringApplication.run(),但上下文加载完成后不会启动 Web 服务器,也不会执行 ApplicationRunner 和 CommandLineRunner。
如果同一个测试套件中有多个测试类使用了 @SpringBootTest 且配置相同,Spring 会缓存 ApplicationContext,后续测试复用同一个上下文,加快测试执行速度。配置不同(如 @MockBean 数量不同或 @TestPropertySource 内容不同)时,Spring 会创建新的上下文。
java
// Test1 加载了上下文
@SpringBootTest
class Test1 { }
// Test2 复用 Test1 的上下文(配置相同)
@SpringBootTest
class Test2 { }
四、如何只加载部分配置?
@SpringBootTest 启动完整上下文在某些场景下过于重量级。可以通过以下方式精简加载内容:
使用 classes 属性指定配置类:
java
@SpringBootTest(classes = {UserService.class, UserMapper.class})
public class UserServiceTest {
// 只加载指定的类
}
配合切片测试:
@WebMvcTest、@DataJpaTest、@MybatisTest 是 @SpringBootTest 的切片变体,只加载特定层次的组件。
@SpringBootTest 和切片测试的选择依据:测试多个层次的协作时用 @SpringBootTest,测试单个层次的逻辑时用切片测试。
五、配置覆盖与属性注入
@TestPropertySource:
在测试类中覆盖配置属性,优先级高于 application.yml。
java
@SpringBootTest
@TestPropertySource(properties = {
"app.timeout=5000",
"app.name=test-app"
})
public class ConfigTest {
@Value("${app.timeout}")
private int timeout;
}
@ActiveProfiles:
指定测试环境激活的 Profile。
java
@SpringBootTest
@ActiveProfiles("test")
public class ProfileTest {
// 使用 application-test.yml 中的配置
}
@TestConfiguration:
定义测试专用的配置类,只在当前测试中生效。
java
@TestConfiguration
public class TestConfig {
@Bean
public UserService mockUserService() {
return mock(UserService.class);
}
}
@SpringBootTest
@Import(TestConfig.class)
public class ServiceTest {
// 测试逻辑
}
六、事务与回滚
默认情况下,@SpringBootTest 不会自动回滚事务。测试执行后数据会持久化到数据库,可能影响后续测试或污染真实数据。
配合 @Transactional 和 @Rollback 可以在测试结束后自动回滚:
java
@SpringBootTest
@Transactional
@Rollback
public class UserServiceTest {
@Test
public void testInsertUser() {
userService.insert(new User("张三"));
// 测试结束后自动回滚
}
}
@Rollback 默认值为 true,如果希望提交事务,可以设置为 false。在 @DataJpaTest 和 @MybatisTest 中,事务回滚是默认行为,不需要额外配置。
七、测试顺序
测试之间不应该有依赖关系。如果某个测试修改了共享状态(如数据库、缓存、静态变量),可能会影响后续测试的执行结果。
确保测试独立性后,如果需要控制测试执行顺序,使用 @TestMethodOrder。
java
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrderedTest {
@Test
@Order(1)
public void testFirst() { }
@Test
@Order(2)
public void testSecond() { }
}
八、常见问题
上下文加载失败:
检查启动类上是否有 @SpringBootApplication,配置文件是否正确,依赖是否完整。
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Autowired 注入失败:
确认被注入的 Bean 已注册到容器中(有 @Component、@Service 等注解),包路径在 @ComponentScan 范围内。
测试速度慢:
使用切片测试替代完整 @SpringBootTest,如 @WebMvcTest 代替 @SpringBootTest 测试 Controller,@DataJpaTest 代替 @SpringBootTest 测试 Repository,@MybatisTest 代替 @SpringBootTest 测试 MyBatis Mapper。
MockBean 不生效:
@MockBean 会替换容器中同类型的 Bean,确保测试中注入的是 Mock 对象。检查是否在同一个测试类中正确使用了 @MockBean 和 @Autowired。
九、最佳实践
@SpringBootTest 是集成测试的首选,但不应滥用。完整上下文的启动时间通常在几秒到十几秒,大量使用会显著拖慢测试反馈周期。
建议按以下原则使用:单个类的逻辑测试用 JUnit + Mockito,不需要 Spring 容器;Service 层和 Mapper 层的协作测试用切片测试(@DataJpaTest、@MybatisTest)加载最小上下文;跨模块的端到端验证用 @SpringBootTest。
在 @SpringBootTest 中,@MockBean 用于隔离外部依赖,模拟需要远程调用或耗时操作的组件。测试完成后,Spring 会销毁上下文,确保不影响其他测试。