
关于
使用 JUnit 5、Mockito、REST Assured、Camel 测试和 JaCoCo 进行 Quarkus 3.x LTS 测试驱动开发。适用于添加功能、修复 Bug 或重构事件驱动服务。
name: quarkus-tdd description: 使用 JUnit 5、Mockito、REST Assured、Camel 测试和 JaCoCo 进行 Quarkus 3.x LTS 的测试驱动开发。在添加功能、修复 bug 或重构事件驱动服务时使用。 origin: ECC
Quarkus TDD 工作流
Quarkus 3.x 服务的 TDD 指导,目标 80%+ 覆盖率(单元 + 集成)。针对使用 Apache Camel 的事件驱动架构优化。
使用场景
- 新功能或 REST 端点
- Bug 修复或重构
- 添加数据访问逻辑、安全规则或响应式流
- 测试 Apache Camel 路由和事件处理器
- 测试使用 RabbitMQ 的事件驱动服务
- 测试条件流逻辑
- 验证 CompletableFuture 异步操作
- 测试 LogContext 传播
工作流
- 先写测试(它们应该失败)
- 实现最少代码使测试通过
- 在测试通过的状态下重构
- 使用 JaCoCo 强制覆盖率(80%+ 目标)
使用 @Nested 组织的单元测试
遵循此结构化方法以获得全面、可读的测试:
@ExtendWith(MockitoExtension.class)
@DisplayName("OrderService Unit Tests")
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@Mock
private EventService eventService;
@Mock
private FulfillmentPublisher fulfillmentPublisher;
@InjectMocks
private OrderService orderService;
private CreateOrderCommand validCommand;
@BeforeEach
void setUp() {
validCommand = new CreateOrderCommand(
"customer-123",
List.of(new OrderLine("sku-123", 2))
);
}
@Nested
@DisplayName("Tests for createOrder")
class CreateOrder {
@Test
@DisplayName("Should persist order and publish fulfillment event")
void givenValidCommand_whenCreateOrder_thenPersistsAndPublishes() {
// ARRANGE
doNothing().when(orderRepository).persist(any(Order.class));
// ACT
OrderReceipt receipt = orderService.createOrder(validCommand);
// ASSERT
assertThat(receipt).isNotNull();
assertThat(receipt.customerId()).isEqualTo("customer-123");
verify(orderRepository).persist(any(Order.class));
verify(fulfillmentPublisher).publishAsync(receipt);
verify(eventService).createSuccessEvent(receipt, "ORDER_CREATED");
}
@Test
@DisplayName("Should reject missing customer id")
void givenMissingCustomerId_whenCreateOrder_thenThrowsBadRequest() {
// ARRANGE
CreateOrderCommand invalid = new CreateOrderCommand("", validCommand.lines());
// ACT & ASSERT
WebApplicationException exception = assertThrows(
WebApplicationException.class,
() -> orderService.createOrder(invalid)
);
assertThat(exception.getResponse().getStatus()).isEqualTo(400);
verify(orderRepository, never()).persist(any(Order.class));
verify(fulfillmentPublisher, never()).publishAsync(any());
}
@Test
@DisplayName("Should record error event when persistence fails")
void givenPersistenceFailure_whenCreateOrder_thenRecordsErrorEvent() {
// ARRANGE
doThrow(new PersistenceException("database unavailable"))
.when(orderRepository).persist(any(Order.class));
// ACT & ASSERT
PersistenceException exception = assertThrows(
PersistenceException.class,
() -> orderService.createOrder(validCommand)
);
assertThat(exception.getMessage()).contains("database unavailable");
verify(eventService).createErrorEvent(
eq(validCommand),
eq("ORDER_CREATE_FAILED"),
contains("database unavailable")
);
verify(fulfillmentPublisher, never()).publishAsync(any());
}
@Test
@DisplayName("Should reject null commands")
void givenNullCommand_whenCreateOrder_thenThrowsNullPointerException() {
// ACT & ASSERT
assertThrows(
NullPointerException.class,
() -> orderService.createOrder(null)
);
verify(orderRepository, never()).persist(any(Order.class));
}
}
}
关键测试模式
- @Nested 类:按被测方法分组测试
- @DisplayName:为测试报告提供可读的测试描述
- 命名约定:
givenX_whenY_thenZ以提高清晰度 - AAA 模式:明确的
// ARRANGE、// ACT、// ASSERT注释 - @BeforeEach:设置通用测试数据以减少重复
- assertDoesNotThrow:测试成功场景而不捕获异常
- assertThrows:使用 AssertJ 进行消息验证的异常场景测试
- 全面覆盖:测试正常路径、空输入、边界情况、异常
- 验证交互:使用 Mockito
verify()确保方法被正确调用 - 从不验证:使用
never()确保方法在错误场景中不被调用
测试 Camel 路由
@QuarkusTest
@DisplayName("Business Rules Camel Route Tests")
class BusinessRulesRouteTest {
@Inject
CamelContext camelContext;
兼容工具
Claude CodeCursor
标签
测试

