集成测试核心是验证多个模块 / 服务 / 中间件协同工作,区别于单元测试(单类 / 单方法)、接口测试(单接口),生产态集成测试更侧重真实环境链路、配置、依赖、事务、并发、异常降级全链路校验。本文结合微服务架构(主流生产形态),从方案、技术栈、实战流程、用例、脚本、踩坑、上线规范全维度讲解。

生产环境严禁直接做破坏性集成测试,行业标准分层:
下文所有「生产环境集成测试」默认指:预发等价生产环境 + 生产冒烟回归。
| 测试类型 | 关注点 | 执行主体 | 运行环境 |
|---|---|---|---|
| 单元测试 | 单个方法 / 逻辑正确性 | 开发 | 本地 / 单元测试容器 |
| 接口测试 | 单接口入参、出参、状态码 | 测试 / 开发 | 测试环境 |
| 集成测试 | 多组件 / 服务 / 中间件链路协同、数据流转、事务一致性 | 开发 + 测试 | 测试 / 预发(类生产) |
| 性能测试 | 并发、吞吐量、响应时间 | 性能测试 | 压测环境 |
spring-boot-starter-test(SpringBoot 项目一站式依赖)<!-- SpringBoot 测试全家桶(包含 JUnit5、Mockito、AssertJ) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- 排除 JUnit4,强制 JUnit5 -->
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 容器化中间件测试(TestContainers,生产集成测试核心) -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<!-- MySQL 容器 -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<!-- Redis 容器 -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>redis</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
@SpringBootTest:标准集成测试入口,完整启动 Spring 上下文,加载所有 Bean、配置、数据源、中间件(模拟真实运行)
classes = 启动类.class、properties = "指定测试配置"@Test:JUnit5 测试方法@DisplayName:给测试类 / 方法加业务名称(生产报告可读性必备)@Transactional:集成测试事务回滚(关键),执行完自动回滚 DB 数据,避免脏数据@AutoConfigureMockMvc:Web 层接口集成测试(模拟 HTTP 请求)@Container / @Testcontainers:启动容器化中间件src/
├── main/ # 业务代码
└── test/
├── unit/ # 单元测试(单类)
├── integrate/ # 【集成测试专属包】按业务模块拆分
│ ├── user/ # 用户模块集成测试
│ ├── order/ # 订单模块集成测试
│ └── mq/ # 消息队列集成测试
└── smoke/ # 生产冒烟测试(极简用例,上线必跑)
按单体应用 → 微服务调用 → 中间件(DB/Redis/MQ)→ 分布式事务 → 异常降级五大生产高频场景实战。
最基础也最常用,验证接口→业务逻辑→数据库完整链路。
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
// 加载完整 Spring 上下文 = 真实运行环境
@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("订单模块 - 全链路集成测试")
public class OrderIntegrateTest {
@Autowired
private MockMvc mockMvc; // 模拟 HTTP 请求
/**
* 新增订单 全链路集成测试
* @Transactional + @Rollback(true):执行后自动回滚DB,生产环境杜绝脏数据
*/
@Test
@Transactional
@Rollback
@DisplayName("正常创建订单-全链路校验")
void createOrderFullLink() throws Exception {
String requestBody = "{"userId":10001,"goodsId":2001,"num":2}";
// 1. 发起接口请求,模拟前端/网关调用
mockMvc.perform(post("/api/order/create")
.contentType("application/json")
.content(requestBody))
// 2. 断言 HTTP 状态码
.andExpect(status().isOk())
// 3. 断言返回码、业务结果
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data.orderNo").exists())
.andExpect(jsonPath("$.data.status").value(0));
// 4. 底层会自动调用 Service -> Mapper -> MySQL,事务结束自动回滚
}
}
@Transactional + @Rollback:预发 / 测试环境防止测试数据污染业务库@SpringBootTest 会加载完整配置(Nacos 配置、多环境配置、Bean 循环依赖等,和生产一致)微服务核心集成点:服务注册、发现、远程调用、参数传递。
@SpringBootTest
@DisplayName("跨服务调用集成测试")
public class FeignIntegrateTest {
// 注入 Feign 远程调用客户端(和业务代码一致)
@Autowired
private UserFeignClient userFeignClient;
@Test
@DisplayName("调用用户服务-查询用户信息")
void callUserService() {
// 1. 执行远程调用(走注册中心、负载均衡、Feign 拦截器)
UserDTO user = userFeignClient.getUserInfo(10001L);
// 2. 断言结果,验证跨服务链路通畅
org.assertj.core.api.Assertions.assertThat(user).isNotNull();
org.assertj.core.api.Assertions.assertThat(user.getUserId()).isEqualTo(10001L);
org.assertj.core.api.Assertions.assertThat(user.getUsername()).isNotBlank();
}
}
禁止使用 H2 内存库做正式集成测试(和生产 MySQL 语法、事务、索引行为不一致),生产统一使用 TestContainers 拉起真实镜像。
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.TestPropertySource;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.RedisContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
// 开启容器测试
@Testcontainers
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test-container.yml")
@DisplayName("MySQL+Redis 中间件集成测试")
public class MiddlewareIntegrateTest {
// 固定 MySQL 容器(全局复用)
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
.withDatabaseName("test_db")
.withUsername("root")
.withPassword("123456");
// 固定 Redis 容器
@Container
static RedisContainer<?> redis = new RedisContainer<>("redis:7.0");
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Test
@DisplayName("Redis 读写集成校验")
void testRedisOperate() {
String key = "test:integrate:key";
String value = "prod-test-data";
// 写入 Redis
redisTemplate.opsForValue().set(key, value);
// 读取并断言
String result = redisTemplate.opsForValue().get(key);
org.assertj.core.api.Assertions.assertThat(result).isEqualTo(value);
}
}
动态读取容器地址,和生产配置格式完全一致:
spring:
datasource:
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/test_db?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
redis:
host: ${REDIS_HOST}
port: ${REDIS_PORT}
验证消息投递、消费、重试、丢包等生产高频问题。 核心思路:发消息 → 监听消费结果 → 断言。
@SpringBootTest
@DisplayName("RocketMQ 消息集成测试")
public class MqIntegrateTest {
@Autowired
private RocketMQTemplate rocketMQTemplate;
// 测试 Topic
private static final String TEST_TOPIC = "test_order_topic";
@Test
@DisplayName("消息发送+消费全链路测试")
void testMqSendAndConsume() throws InterruptedException {
String msgBody = "order_10086";
// 1. 发送消息(模拟业务投递)
rocketMQTemplate.syncSend(TEST_TOPIC, msgBody);
// 2. 等待消费(生产根据消费耗时调整)
Thread.sleep(1000);
// 3. 业务侧:查询数据库/缓存,验证消息是否被正常消费、数据是否更新
boolean consumeSuccess = checkOrderStatus("10086");
org.assertj.core.api.Assertions.assertThat(consumeSuccess).isTrue();
}
// 模拟校验消费结果(实际查DB/Redis)
private boolean checkOrderStatus(String orderNo) {
// 业务校验逻辑
return true;
}
}
微服务生产重点:AT/TCC 事务、跨服务回滚。 集成测试核心:主动制造异常,校验事务是否全局回滚。
@SpringBootTest
@DisplayName("Seata 分布式事务集成测试")
public class SeataTransactionTest {
@Autowired
private OrderService orderService;
@Test
@DisplayName("分布式事务异常-全局回滚校验")
void testDistributedRollback() {
try {
// 该方法内部:创建订单 + 扣库存 + 模拟异常
orderService.createOrderWithError(10001L, 2001L);
} catch (Exception e) {
// 捕获异常,继续校验数据
}
// 断言:订单、库存数据全部回滚,无脏数据
boolean orderExist = orderMapper.existsOrder(10001L);
int stock = stockMapper.getStock(2001L);
org.assertj.core.api.Assertions.assertThat(orderExist).isFalse();
org.assertj.core.api.Assertions.assertThat(stock).isEqualTo(原始库存);
}
}
验证生产容错能力:接口被限流、服务熔断后,降级逻辑是否正常执行。
@SpringBootTest
@DisplayName("Sentinel 熔断降级集成测试")
public class SentinelDegradeTest {
@Autowired
private OrderApi orderApi;
@Test
@DisplayName("服务熔断-降级逻辑校验")
void testDegrade() {
// 多次调用触发熔断(模拟生产服务不可用)
for (int i = 0; i < 20; i++) {
String result = orderApi.getOrderInfo(9999L);
// 断言:返回降级兜底数据
org.assertj.core.api.Assertions.assertThat(result).contains("服务繁忙,请稍后重试");
}
}
}
公司标准流水线: 代码提交 GitLab → 触发 Jenkins/GitLab CI → 编译项目 → 自动执行全量集成测试 → 结果上报
预发 = 1:1 生产配置、数据、中间件、网络。执行三类测试:
生产禁止全量集成测试,只做冒烟测试(最小集成用例集):
修复 Bug、迭代版本后,针对性跑相关模块集成用例,防止改出连带问题。
createOrder_正常下单、payOrder_超时触发重试@MockBean 局部隔离@Transactional + @Rollback;定时清理测试库历史数据application.yml、Nacos 配置、中间件版本、防火墙 / 白名单