CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkus--quarkus-test-common

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

Pending
Overview
Eval results
Files

test-annotations.mddocs/

Core Test Annotations

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.

Capabilities

Mock Annotation

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());
    }
}

InjectMock Annotation

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");
    }
}

TestTransaction Annotation

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());
    }
}

TestReactiveTransaction Annotation

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();
    }
}

ActivateSessionContext Annotation

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);
    }
}

TestMethodInvoker Interface

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());
    }
}

Advanced Usage Patterns

Combining Annotations

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
    }
}

Mock Alternatives and Spies

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());
    }
}

Transaction Isolation Testing

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

docs

http-testing.md

index.md

launchers.md

test-annotations.md

test-context.md

test-resources.md

utilities.md

tile.json