Java 测试¶
此文件使用 Java 特定内容扩展 common/testing.md。
测试框架¶
- JUnit 5(
@Test、@ParameterizedTest、@Nested、@DisplayName) - AssertJ — 流畅断言(
assertThat(result).isEqualTo(expected)) - Mockito — 依赖项模拟
- Testcontainers — 需要数据库或服务的集成测试
测试结构¶
src/test/java/com/example/app/
service/ # サービス層のユニットテスト
controller/ # Web 層 / API テスト
repository/ # データアクセステスト
integration/ # クロスレイヤー統合テスト
将 src/main/java 中的包结构镜像到 src/test/java。
单元测试模式¶
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
private OrderService orderService;
@BeforeEach
void setUp() {
orderService = new OrderService(orderRepository);
}
@Test
@DisplayName("findById returns order when exists")
void findById_existingOrder_returnsOrder() {
var order = new Order(1L, "Alice", BigDecimal.TEN);
when(orderRepository.findById(1L)).thenReturn(Optional.of(order));
var result = orderService.findById(1L);
assertThat(result.customerName()).isEqualTo("Alice");
verify(orderRepository).findById(1L);
}
@Test
@DisplayName("findById throws when order not found")
void findById_missingOrder_throws() {
when(orderRepository.findById(99L)).thenReturn(Optional.empty());
assertThatThrownBy(() -> orderService.findById(99L))
.isInstanceOf(OrderNotFoundException.class)
.hasMessageContaining("99");
}
}
参数化测试¶
@ParameterizedTest
@CsvSource({
"100.00, 10, 90.00",
"50.00, 0, 50.00",
"200.00, 25, 150.00"
})
@DisplayName("discount applied correctly")
void applyDiscount(BigDecimal price, int pct, BigDecimal expected) {
assertThat(PricingUtils.discount(price, pct)).isEqualByComparingTo(expected);
}
集成测试¶
使用 Testcontainers 进行真实数据库集成:
@Testcontainers
class OrderRepositoryIT {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
private OrderRepository repository;
@BeforeEach
void setUp() {
var dataSource = new PGSimpleDataSource();
dataSource.setUrl(postgres.getJdbcUrl());
dataSource.setUser(postgres.getUsername());
dataSource.setPassword(postgres.getPassword());
repository = new JdbcOrderRepository(dataSource);
}
@Test
void save_and_findById() {
var saved = repository.save(new Order(null, "Bob", BigDecimal.ONE));
var found = repository.findById(saved.getId());
assertThat(found).isPresent();
}
}
有关 Spring Boot 集成测试,请参见技能:springboot-tdd。
有关 Quarkus 集成测试,请参见技能:quarkus-tdd。
测试命名¶
使用 @DisplayName 编写描述性名称:
- 方法名使用 methodName_scenario_expectedBehavior()
- 报告中使用 @DisplayName("人間が読める説明")
覆盖率¶
- 目标行覆盖率 80% 以上
- 覆盖率报告使用 JaCoCo
- 专注于服务和领域逻辑 — 跳过显而易见的 getter/配置类
参考¶
请参见技能:springboot-tdd,了解使用 MockMvc 和 Testcontainers 的 Spring Boot TDD 模式。
请参见技能:quarkus-tdd,了解使用 REST Assured 和 Dev Services 的 Quarkus TDD 模式。
请参见技能:java-coding-standards,了解测试要求。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC