本文目录导读:

我来详细说明SpringBoot中常用的单元测试注解及其使用方式。
核心测试注解
@SpringBootTest
// 基础用法 - 加载完整的Spring应用上下文
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testFindUser() {
User user = userService.findById(1L);
assertNotNull(user);
}
}
// 指定配置类和属性
@SpringBootTest(
classes = Application.class,
properties = {"spring.profiles.active=test"}
)
class ServiceTest {
// 测试代码
}
@WebMvcTest
// 测试Controller层,只加载Web相关组件
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser() throws Exception {
// 模拟Service层返回
when(userService.findById(1L)).thenReturn(new User(1L, "张三"));
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("张三"));
}
}
@DataJpaTest
// 测试JPA Repository层
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.ANY)
class UserRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
@Test
void testFindByUsername() {
// 插入测试数据
entityManager.persist(new User("testuser", "test@example.com"));
// 执行查询
User user = userRepository.findByUsername("testuser");
// 验证结果
assertEquals("test@example.com", user.getEmail());
}
}
@RestClientTest
// 测试REST客户端
@RestClientTest(UserApiClient.class)
class UserApiClientTest {
@Autowired
private MockRestServiceServer server;
@Autowired
private UserApiClient client;
@Test
void testGetUser() {
server.expect(requestTo("/api/users/1"))
.andRespond(withSuccess(
"{\"id\":1,\"name\":\"张三\"}",
MediaType.APPLICATION_JSON
));
User user = client.getUser(1L);
assertEquals("张三", user.getName());
}
}
辅助测试注解
@MockBean 和 @SpyBean
@SpringBootTest
class OrderServiceTest {
// 创建Mock,替换Spring容器中的Bean
@MockBean
private PaymentService paymentService;
// 创建Spy,保留部分真实行为
@SpyBean
private LogService logService;
@Test
void testCreateOrder() {
// Mock方法行为
when(paymentService.processPayment(any())).thenReturn(true);
// 调用被测试方法
orderService.createOrder(new OrderDTO());
// 验证Mock方法被调用
verify(paymentService).processPayment(any());
}
}
@JsonTest
// 测试JSON序列化和反序列化
@JsonTest
class UserJsonTest {
@Autowired
private JacksonTester<User> json;
@Test
void testSerialize() throws Exception {
User user = new User(1L, "张三", 25);
// 序列化测试
assertThat(json.write(user))
.isEqualToJson("user.json");
// 反序列化测试
String content = "{\"id\":1,\"name\":\"张三\",\"age\":25}";
assertThat(json.parse(content))
.isEqualTo(new User(1L, "张三", 25));
}
}
配置相关注解
@TestConfiguration
// 为测试提供特定的配置
@SpringBootTest
class ServiceTest {
// 测试专用的配置类
@TestConfiguration
static class TestConfig {
@Bean
@Primary
public DatabaseService testDatabaseService() {
return new InMemoryDatabaseService();
}
}
@Autowired
private DatabaseService databaseService;
@Test
void testWithCustomConfig() {
// 使用测试专用配置
}
}
@ActiveProfiles
// 激活特定的Spring Profile
@SpringBootTest
@ActiveProfiles("test")
class ServiceTest {
@Value("${app.test.property}")
private String testProperty;
@Test
void testWithTestProfile() {
// 使用test profile的配置
assertEquals("test-value", testProperty);
}
}
事务和数据库相关
@Transactional 和 @Rollback
@SpringBootTest
@Transactional // 默认回滚事务
class UserServiceTest {
@Autowired
private UserService userService;
@Test
@Rollback(false) // 不自动回滚
void testSaveUserWithNoRollback() {
userService.save(new User("test", "test@test.com"));
}
@Test
@Commit // 提交事务
void testSaveUserWithCommit() {
userService.save(new User("test2", "test2@test.com"));
}
}
@Sql 和 @SqlGroup
@SpringBootTest
@Sql(scripts = "/sql/init-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
class UserRepositoryTest {
@Test
void testWithSqlInitData() {
// 测试数据已由@Sql初始化
}
@Test
@SqlGroup({
@Sql("/sql/special-data.sql"),
@Sql(statements = "INSERT INTO users VALUES(100, 'special', 'special@test.com')")
})
void testWithMultipleSqlSources() {
// 使用多个SQL数据源
}
}
实际测试示例
完整的Service层测试
@ExtendWith(MockitoExtension.class) // JUnit 5
// 或 @RunWith(SpringRunner.class) // JUnit 4
@SpringBootTest
class OrderServiceCompleteTest {
@Autowired
private OrderService orderService;
@MockBean
private PaymentService paymentService;
@MockBean
private StockService stockService;
@MockBean
private NotificationService notificationService;
@Test
void testCreateOrderSuccess() {
// 准备测试数据
OrderDTO orderDTO = new OrderDTO();
orderDTO.setUserId(1L);
orderDTO.setProductId(100L);
orderDTO.setQuantity(2);
// 模拟依赖服务
when(stockService.checkStock(100L, 2)).thenReturn(true);
when(paymentService.processPayment(any())).thenReturn(true);
doNothing().when(notificationService).sendOrderConfirmation(any());
// 执行测试
Order order = orderService.createOrder(orderDTO);
// 验证结果
assertNotNull(order);
assertEquals(OrderStatus.CREATED, order.getStatus());
// 验证交互
verify(stockService).checkStock(100L, 2);
verify(paymentService).processPayment(any());
verify(notificationService).sendOrderConfirmation(any());
}
@Test
void testCreateOrderInsufficientStock() {
// 模拟库存不足
when(stockService.checkStock(100L, 999)).thenReturn(false);
// 验证异常
assertThrows(InsufficientStockException.class, () -> {
orderService.createOrder(new OrderDTO());
});
// 验证支付没有被调用
verify(paymentService, never()).processPayment(any());
}
}
API集成测试
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class UserApiIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
void testCreateUserApi() throws Exception {
// 准备请求数据
UserCreateDTO createDTO = new UserCreateDTO();
createDTO.setUsername("newuser");
createDTO.setEmail("newuser@test.com");
createDTO.setPassword("password123");
// 发送POST请求
MvcResult result = mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(createDTO)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").isNumber())
.andExpect(jsonPath("$.username").value("newuser"))
.andReturn();
// 解析响应
String responseBody = result.getResponse().getContentAsString();
UserResponseDTO response = objectMapper.readValue(responseBody, UserResponseDTO.class);
// 验证结果
assertNotNull(response.getId());
assertEquals("newuser", response.getUsername());
}
@Test
void testGetUserNotFound() throws Exception {
mockMvc.perform(get("/api/users/99999"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.error").value("User not found"));
}
}
注解使用建议
选择合适的注解
- @SpringBootTest:完整的集成测试,加载所有Bean
- @WebMvcTest:Controller测试,只加载Web层
- @DataJpaTest:JPA Repository测试
- @RestClientTest:REST客户端测试
- @JsonTest:JSON序列化/反序列化测试
性能优化建议
- 使用切片测试(Slice Test)减少加载的Bean数量
- 使用
@MockBean替代真实的Bean - 使用内存数据库(H2)进行数据库测试
- 合理使用
@DirtiesContext控制上下文刷新
这些注解的组合使用可以构建高效、可维护的SpringBoot单元测试,根据测试需求选择合适的注解,可以提高测试速度和代码质量。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播