本文目录导读:

我来详细介绍SpringBoot集成测试的模拟环境搭建和常用方案。
测试环境搭建依赖
Maven依赖配置
<!-- Spring Boot Test -->
<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>
<!-- 如果需要Web环境 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<!-- 数据库测试内存库 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<!-- Testcontainers (容器化测试) -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
测试模拟方案
Mockito模拟
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testFindUserById() {
// 模拟数据
User mockUser = new User(1L, "张三", "zhangsan@test.com");
Mockito.when(userRepository.findById(1L))
.thenReturn(Optional.of(mockUser));
// 执行测试
User result = userService.findUserById(1L);
// 验证结果
assertEquals("张三", result.getName());
Mockito.verify(userRepository).findById(1L);
}
}
Spring Boot集成测试
@SpringBootTest
@AutoConfigureMockMvc
@Transactional // 测试后自动回滚
public class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private UserRepository userRepository;
@Test
void testCreateUser() throws Exception {
// 准备测试数据
User user = new User();
user.setName("测试用户");
user.setEmail("test@example.com");
// 执行请求
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("测试用户"));
}
private String asJsonString(Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
切片测试
// Web层测试
@WebMvcTest(UserController.class)
public class UserControllerSliceTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser() throws Exception {
// 模拟Service层
Mockito.when(userService.findUserById(1L))
.thenReturn(new User(1L, "张三", "test@test.com"));
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("张三"));
}
}
// 数据层测试
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
@Test
void testFindByEmail() {
// 准备数据
User user = new User("张三", "test@test.com");
entityManager.persist(user);
entityManager.flush();
// 执行查询
Optional<User> found = userRepository.findByEmail("test@test.com");
// 验证结果
assertTrue(found.isPresent());
assertEquals("张三", found.get().getName());
}
}
数据库模拟
H2内存数据库
# application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
database-platform: org.hibernate.dialect.H2Dialect
h2:
console:
enabled: true
Testcontainers容器化测试
@SpringBootTest
@Testcontainers
public class DatabaseContainerTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:14")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void testDatabaseInteraction() {
// 测试实际的数据库操作
User user = new User("张三", "test@test.com");
userRepository.save(user);
assertNotNull(user.getId());
}
}
外部服务模拟
Redis模拟
@SpringBootTest
@Testcontainers
public class RedisTest {
@Container
static GenericContainer<?> redis = new GenericContainer<>("redis:6-alpine")
.withExposedPorts(6379);
@DynamicPropertySource
static void configureRedis(DynamicPropertyRegistry registry) {
registry.add("spring.redis.host", redis::getHost);
registry.add("spring.redis.port", redis::getFirstMappedPort);
}
@Autowired
private StringRedisTemplate redisTemplate;
@Test
void testRedisOperations() {
redisTemplate.opsForValue().set("key", "value");
assertEquals("value", redisTemplate.opsForValue().get("key"));
}
}
外部API Mock
@SpringBootTest
@AutoConfigureMockMvc
public class ExternalApiTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private ExternalApiService externalApiService;
@Test
void testExternalCall() throws Exception {
// 模拟外部API响应
Mockito.when(externalApiService.callExternalApi())
.thenReturn(new ExternalResponse("success", 200));
mockMvc.perform(post("/api/process")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"data\":\"test\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("success"));
}
}
WireMock模拟HTTP服务
@SpringBootTest
@WireMockTest(httpPort = 8089)
public class WireMockTest {
@Test
void testExternalHttpCall() {
// 配置WireMock响应
stubFor(get(urlEqualTo("/api/data"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody("{\"id\":1, \"name\":\"test\"}")));
// 测试代码
ResponseEntity<String> response = restTemplate.getForEntity(
"http://localhost:8089/api/data", String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
Spring Cloud组件模拟
配置中心模拟
@SpringBootTest
@EnableConfigurationProperties
@ContextConfiguration(initializers = ConfigDataInitializer.class)
public class ConfigTest {
@Value("${test.property}")
private String testProperty;
@Test
void testConfigProperty() {
assertEquals("test-value", testProperty);
}
}
服务发现模拟
@SpringBootTest
@AutoConfigureMockMvc
public class ServiceDiscoveryTest {
@MockBean
private DiscoveryClient discoveryClient;
@Test
void testServiceDiscovery() {
// 模拟服务发现
List<ServiceInstance> instances = Arrays.asList(
new DefaultServiceInstance("service1", "user-service", "localhost", 8080, false)
);
Mockito.when(discoveryClient.getInstances("user-service"))
.thenReturn(instances);
// 执行测试
List<ServiceInstance> result = discoveryClient.getInstances("user-service");
assertEquals(1, result.size());
}
}
测试最佳实践
测试配置隔离
@TestConfiguration
public class TestConfig {
@Bean
@Primary
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
@Bean
@Primary
public RestTemplate testRestTemplate() {
return new RestTemplate();
}
}
测试分类和过滤
// 定义测试分类
@Tag("integration")
@Tag("database")
public class DatabaseIntegrationTest {
// 测试代码
}
// 运行特定标签的测试
// mvn test -Dgroups="integration"
测试前后置处理
@SpringBootTest
public class TestLifecycleTest {
@BeforeAll
static void initAll() {
// 全局初始化
}
@BeforeEach
void init() {
// 每个测试方法前执行
}
@Test
void testSomething() {
// 测试逻辑
}
@AfterEach
void tearDown() {
// 每个测试方法后执行
}
@AfterAll
static void tearDownAll() {
// 全局清理
}
}
性能考虑
- 测试范围选择:控制测试范围,避免不必要的上下文加载
- 并行测试:合理配置并行执行策略
- 资源清理:确保测试后资源被正确释放
- 依赖管理:管理好测试环境的依赖版本
这个测试模拟方案可以帮助你在开发过程中快速验证业务逻辑,同时减少对外部环境的依赖,根据项目实际需求,选择合适的测试方案组合。