@SpringBootTest 详解:Spring Boot 测试的核心注解

@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);
    }
}

MOCKRANDOM_PORT 的区别在于:MOCK 模拟 Servlet 环境,MockMvc 发起的请求不经过网络,直接由 DispatcherServlet 处理;RANDOM_PORT 启动真实 Tomcat,请求经过完整的网络栈。

三、@SpringBootTest 的加载机制

@SpringBootTest 在测试启动时,会根据当前类路径和配置推断应用类型(Servlet、Reactive 或非 Web),然后创建对应的 ApplicationContext。它会搜索 @SpringBootApplication@SpringBootConfiguration 标注的类作为配置源,加载所有自动配置。

加载过程相当于执行了一次 SpringApplication.run(),但上下文加载完成后不会启动 Web 服务器,也不会执行 ApplicationRunnerCommandLineRunner

如果同一个测试套件中有多个测试类使用了 @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 会销毁上下文,确保不影响其他测试。

相关推荐
分支预测失败15 分钟前
RISC-V 虚拟内存与 MMU 实战:从 Sv39 页表到 Linux 地址空间
后端
对象存储与RustFS21 分钟前
给 RustFS 拆多租户权限:IAM 用户、组与策略的实战
后端·rust·开源
ZGG0031 小时前
MCP 02:MCP 的各类比对——和 Function Calling、A2A、Skill 到底什么关系
人工智能·后端·python
IT_陈寒1 小时前
Vite热更新突然失效?可能是这个配置在捣鬼
前端·人工智能·后端
SelectDB技术团队2 小时前
统一全文检索与 SQL 分析:Apache Doris 日志分析实践
大数据·数据结构·后端·python·全文检索·doris·日志分析
霸道流氓气质2 小时前
Spring AI多模型路由与动态切换
java·后端·spring
艺杯羹2 小时前
全栈信创落地实录:基于银河麒麟V10与达梦数据库DM8的SpringBoot工业级适配指南
java·数据库·spring boot·后端·spring
学长毕业设计10 小时前
基于SpringBoot的公益基金管理系统(源码+文档+讲解视频)
java·spring boot·后端
东小西10 小时前
【SAA实战】第 3 篇 · 工具调用全攻略:把业务能力交给 Agent 自己调度
java·后端·spring