Common test utilities and framework for Quarkus applications, providing core testing infrastructure including application launchers, test isolation, configuration management, and integration with REST Assured for HTTP testing
—
Essential annotations for mocking, transaction management, and session context control in Quarkus tests. These annotations provide declarative control over test behavior and automatically integrate with the Quarkus testing framework.
Built-in stereotype for creating mock beans in the CDI container during test execution.
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Stereotype
public @interface Mock {}Usage Example:
public interface UserService {
User findById(Long id);
User save(User user);
}
@Mock
@ApplicationScoped
public class MockUserService implements UserService {
@Override
public User findById(Long id) {
return new User(id, "Mock User", "mock@example.com");
}
@Override
public User save(User user) {
user.setId(99L);
return user;
}
}
public class UserControllerTest {
@Inject
UserService userService; // Automatically receives MockUserService
@Test
public void testMockService() {
User user = userService.findById(1L);
assertEquals("Mock User", user.getName());
}
}Instructs the test engine to inject mock instances, typically used with mocking frameworks like Mockito.
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectMock {}Usage with Mockito:
public class OrderServiceTest {
@InjectMock
PaymentService paymentService;
@InjectMock
InventoryService inventoryService;
@Inject
OrderService orderService; // Real service with mocked dependencies
@Test
public void testOrderProcessing() {
// Setup mocks
when(inventoryService.isAvailable("ITEM_001")).thenReturn(true);
when(paymentService.processPayment(any())).thenReturn(true);
// Test with mocked dependencies
Order order = new Order("ITEM_001", 2);
OrderResult result = orderService.processOrder(order);
assertTrue(result.isSuccess());
verify(paymentService).processPayment(any());
verify(inventoryService).isAvailable("ITEM_001");
}
}Runs the annotated method in a rollback-only JTA transaction, ensuring database changes are automatically reverted.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@InterceptorBinding
public @interface TestTransaction {}Usage Example:
public class UserRepositoryTest {
@Inject
UserRepository userRepository;
@Test
@TestTransaction
public void testUserCreation() {
// Any database changes are automatically rolled back
User user = new User("Alice", "alice@example.com");
userRepository.persist(user);
assertNotNull(user.getId());
// Verify user exists within transaction
User found = userRepository.findById(user.getId());
assertEquals("Alice", found.getName());
// Changes will be rolled back after method completes
}
@Test
public void testUserDoesNotExist() {
// User from previous test was rolled back
List<User> users = userRepository.listAll();
assertTrue(users.isEmpty());
}
}Runs the annotated method in a rollback-only reactive transaction for reactive data access.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@InterceptorBinding
public @interface TestReactiveTransaction {}Usage with Reactive Panache:
public class ReactiveUserRepositoryTest {
@Test
@TestReactiveTransaction
public Uni<Void> testReactiveUserCreation() {
User user = new User("Bob", "bob@example.com");
return user.persist()
.chain(ignored -> {
assertNotNull(user.getId());
return User.findById(user.getId());
})
.invoke(found -> {
assertEquals("Bob", found.getName());
// Transaction will be rolled back
})
.replaceWithVoid();
}
}Activates the HTTP session context before method execution, useful for testing session-scoped beans.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@InterceptorBinding
public @interface ActivateSessionContext {}Usage Example:
@SessionScoped
public class ShoppingCart implements Serializable {
private List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
public List<Item> getItems() {
return items;
}
}
public class ShoppingCartTest {
@Inject
ShoppingCart cart;
@Test
@ActivateSessionContext
public void testSessionScopedCart() {
// Session context is automatically activated
cart.addItem(new Item("Book", 29.99));
cart.addItem(new Item("Pen", 1.99));
assertEquals(2, cart.getItems().size());
assertEquals(31.98, cart.getItems().stream()
.mapToDouble(Item::getPrice).sum(), 0.01);
}
}SPI for altering test method invocation, allowing custom parameter injection and method execution control.
public interface TestMethodInvoker {
boolean handlesMethodParamType(String paramClassName);
Object methodParamInstance(String paramClassName);
boolean supportsMethod(Class<?> originalTestClass, Method originalTestMethod);
Object invoke(Object actualTestInstance, Method actualTestMethod,
List<Object> actualTestMethodArgs, String testClassName) throws Throwable;
}Custom Test Method Invoker:
public class CustomParameterInvoker implements TestMethodInvoker {
@Override
public boolean handlesMethodParamType(String paramClassName) {
return "com.example.TestContext".equals(paramClassName);
}
@Override
public Object methodParamInstance(String paramClassName) {
if ("com.example.TestContext".equals(paramClassName)) {
return new TestContext("test-environment");
}
return null;
}
@Override
public boolean supportsMethod(Class<?> originalTestClass, Method originalTestMethod) {
return Arrays.stream(originalTestMethod.getParameterTypes())
.anyMatch(type -> type.getName().equals("com.example.TestContext"));
}
@Override
public Object invoke(Object actualTestInstance, Method actualTestMethod,
List<Object> actualTestMethodArgs, String testClassName) throws Throwable {
// Custom method invocation logic
System.out.println("Invoking test with custom context: " + testClassName);
return actualTestMethod.invoke(actualTestInstance, actualTestMethodArgs.toArray());
}
}
// Usage in test:
public class CustomParameterTest {
@Test
public void testWithCustomParameter(TestContext context) {
// TestContext is automatically injected by CustomParameterInvoker
assertEquals("test-environment", context.getEnvironment());
}
}Multiple annotations can be combined for comprehensive test setup:
public class ComprehensiveTest {
@InjectMock
ExternalService externalService;
@Test
@TestTransaction
@ActivateSessionContext
public void testWithMultipleContexts(TestContext context) {
// Setup mocks
when(externalService.getData()).thenReturn("mock-data");
// Test logic with transaction, session, and custom parameter
// All contexts are properly activated and cleaned up
}
}Using different mocking strategies:
public class AdvancedMockingTest {
@InjectMock
UserService userService;
@Test
public void testWithSpy() {
// Convert mock to spy for partial mocking
UserService spy = Mockito.spy(userService);
doReturn(true).when(spy).isValidUser(any());
// Call real methods for other operations
}
@Test
public void testWithAnswer() {
when(userService.findById(any())).thenAnswer(invocation -> {
Long id = invocation.getArgument(0);
return new User(id, "User " + id, id + "@example.com");
});
User user1 = userService.findById(1L);
User user2 = userService.findById(2L);
assertEquals("User 1", user1.getName());
assertEquals("User 2", user2.getName());
}
}Testing different transaction scenarios:
public class TransactionTest {
@Inject
UserRepository userRepository;
@Test
@TestTransaction
public void testTransactionalBehavior() {
User user = new User("Charlie", "charlie@example.com");
userRepository.persist(user);
// Within transaction, user exists
assertTrue(userRepository.isPersistent(user));
// But will be rolled back after test
}
@Test
public void testNonTransactionalBehavior() {
// No @TestTransaction - changes persist
User user = new User("David", "david@example.com");
userRepository.persist(user);
// Need manual cleanup in @AfterEach
}
@AfterEach
public void cleanup() {
// Manual cleanup for non-transactional tests
userRepository.deleteAll();
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-test-common