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-context.mddocs/

Test Context and Status

Access to test execution context, Dev Services properties, and test failure status for advanced test scenarios and resource cleanup. These components provide deep integration with the Quarkus test lifecycle and development services.

Capabilities

TestStatus Class

Represents test execution status with access to failure information for conditional resource cleanup and debugging.

public class TestStatus {
    public TestStatus(Throwable testErrorCause) {}
    public Throwable getTestErrorCause() {}
    public boolean isTestFailed() {}
}

Usage in Test Resources:

public class LoggingTestResource implements QuarkusTestResourceLifecycleManager {
    private TestStatus testStatus;
    
    @Override
    public void setContext(Context context) {
        this.testStatus = context.getTestStatus();
    }
    
    @Override
    public void stop() {
        if (testStatus.isTestFailed()) {
            // Capture additional logs for failed tests
            captureFailureLogs();
            
            Throwable cause = testStatus.getTestErrorCause();
            if (cause != null) {
                System.err.println("Test failed with: " + cause.getMessage());
                cause.printStackTrace();
            }
        }
    }
    
    private void captureFailureLogs() {
        // Implementation to capture logs, database state, etc.
        System.out.println("Capturing failure diagnostics...");
    }
}

DevServicesContext Interface

Provides access to Dev Services properties and container networking information for test resource coordination.

public interface DevServicesContext {
    Map<String, String> devServicesProperties();
    Optional<String> containerNetworkId();
    
    interface ContextAware {
        void setIntegrationTestContext(DevServicesContext context);
    }
}

Usage Example:

public class DatabaseAwareTestResource implements QuarkusTestResourceLifecycleManager, 
                                                  DevServicesContext.ContextAware {
    private DevServicesContext devServicesContext;
    
    @Override
    public void setIntegrationTestContext(DevServicesContext context) {
        this.devServicesContext = context;
    }
    
    @Override
    public Map<String, String> start() {
        // Access Dev Services database properties
        Map<String, String> devServicesProps = devServicesContext.devServicesProperties();
        String dbUrl = devServicesProps.get("quarkus.datasource.jdbc.url");
        
        if (dbUrl != null) {
            System.out.println("Using Dev Services database: " + dbUrl);
            
            // Use the same network for additional containers
            Optional<String> networkId = devServicesContext.containerNetworkId();
            if (networkId.isPresent()) {
                return configureWithNetwork(networkId.get());
            }
        }
        
        return Map.of();
    }
    
    private Map<String, String> configureWithNetwork(String networkId) {
        // Configure additional test containers to use the same network
        // This allows containers to communicate with Dev Services
        GenericContainer<?> additionalService = new GenericContainer<>("redis:7")
            .withNetwork(Network.newNetwork())
            .withExposedPorts(6379);
        
        additionalService.start();
        
        return Map.of(
            "quarkus.redis.hosts", "redis://localhost:" + additionalService.getMappedPort(6379)
        );
    }
}

ListeningAddress Class

Represents server listening address information for test URL construction and service discovery.

public class ListeningAddress {
    public ListeningAddress(Integer port, String protocol) {}
    public Integer getPort() {}
    public String getProtocol() {}
    public boolean isSsl() {}
}

Usage Example:

public class ServiceDiscoveryTest {
    @Test
    public void testServiceEndpoints() {
        // Get application listening addresses
        ListeningAddress httpAddress = new ListeningAddress(8080, "http");
        ListeningAddress httpsAddress = new ListeningAddress(8443, "https");
        
        // Test HTTP endpoint
        assertFalse(httpAddress.isSsl());
        assertEquals("http", httpAddress.getProtocol());
        
        String httpUrl = httpAddress.getProtocol() + "://localhost:" + httpAddress.getPort();
        given()
            .baseUri(httpUrl)
        .when()
            .get("/health")
        .then()
            .statusCode(200);
        
        // Test HTTPS endpoint
        assertTrue(httpsAddress.isSsl());
        assertEquals("https", httpsAddress.getProtocol());
        
        String httpsUrl = httpsAddress.getProtocol() + "://localhost:" + httpsAddress.getPort();
        given()
            .baseUri(httpsUrl)
            .relaxedHTTPSValidation()
        .when()
            .get("/health")
        .then()
            .statusCode(200);
    }
}

Advanced Usage Patterns

Conditional Test Resource Behavior

Using test status for conditional resource management:

public class ConditionalCleanupResource implements QuarkusTestResourceLifecycleManager {
    private ExternalService externalService;
    private TestStatus testStatus;
    
    @Override
    public void setContext(Context context) {
        this.testStatus = context.getTestStatus();
    }
    
    @Override
    public Map<String, String> start() {
        externalService = new ExternalService();
        externalService.start();
        
        return Map.of(
            "external.service.url", externalService.getUrl()
        );
    }
    
    @Override
    public void stop() {
        if (testStatus.isTestFailed()) {
            // Preserve resources for debugging
            System.out.println("Test failed - preserving external service for debugging");
            System.out.println("Service URL: " + externalService.getUrl());
            System.out.println("Admin URL: " + externalService.getAdminUrl());
            
            // Optionally create debug snapshot
            externalService.createDebugSnapshot();
        } else {
            // Normal cleanup
            externalService.stop();
        }
    }
}

Dev Services Integration

Integrating with existing Dev Services containers:

public class DevServicesIntegrationResource implements QuarkusTestResourceLifecycleManager,
                                                       DevServicesContext.ContextAware {
    private DevServicesContext context;
    private GenericContainer<?> additionalContainer;
    
    @Override
    public void setIntegrationTestContext(DevServicesContext context) {
        this.context = context;
    }
    
    @Override
    public Map<String, String> start() {
        Map<String, String> config = new HashMap<>();
        
        // Get Dev Services properties
        Map<String, String> devProps = context.devServicesProperties();
        
        // Check if Dev Services PostgreSQL is running
        String dbUrl = devProps.get("quarkus.datasource.jdbc.url");
        if (dbUrl != null && dbUrl.contains("postgresql")) {
            // Start additional container in same network
            Optional<String> networkId = context.containerNetworkId();
            
            if (networkId.isPresent()) {
                additionalContainer = new GenericContainer<>("adminer:4")
                    .withNetwork(Network.newNetwork())
                    .withExposedPorts(8080)
                    .withEnv("ADMINER_DEFAULT_SERVER", extractHostFromUrl(dbUrl));
                
                additionalContainer.start();
                
                config.put("adminer.url", 
                    "http://localhost:" + additionalContainer.getMappedPort(8080));
            }
        }
        
        return config;
    }
    
    @Override
    public void stop() {
        if (additionalContainer != null) {
            additionalContainer.stop();
        }
    }
    
    private String extractHostFromUrl(String jdbcUrl) {
        // Extract hostname from JDBC URL for container networking
        return jdbcUrl.replaceAll(".*//([^:/]+).*", "$1");
    }
}

Test Profile Context

Using test profile information for conditional behavior:

public class ProfileAwareResource implements QuarkusTestResourceLifecycleManager {
    private String testProfile;
    
    @Override
    public void setContext(Context context) {
        this.testProfile = context.testProfile();
    }
    
    @Override
    public Map<String, String> start() {
        Map<String, String> config = new HashMap<>();
        
        switch (testProfile) {
            case "integration":
                // Start full external services
                config.putAll(startIntegrationServices());
                break;
                
            case "performance":
                // Start monitoring and metrics services
                config.putAll(startPerformanceServices());
                break;
                
            case "security":
                // Start security test services
                config.putAll(startSecurityServices());
                break;
                
            default:
                // Minimal setup for unit tests
                config.putAll(startMinimalServices());
        }
        
        return config;
    }
    
    private Map<String, String> startIntegrationServices() {
        // Start database, message broker, external APIs
        return Map.of(
            "postgres.url", startPostgres(),
            "kafka.url", startKafka(),
            "external.api.url", startMockAPI()
        );
    }
    
    private Map<String, String> startPerformanceServices() {
        // Start monitoring stack
        return Map.of(
            "prometheus.url", startPrometheus(),
            "grafana.url", startGrafana()
        );
    }
    
    // Additional profile-specific configurations...
}

Multi-Container Coordination

Coordinating multiple containers with shared networking:

public class MultiContainerResource implements QuarkusTestResourceLifecycleManager,
                                               DevServicesContext.ContextAware {
    private DevServicesContext context;
    private Network sharedNetwork;
    private GenericContainer<?> databaseContainer;
    private GenericContainer<?> cacheContainer;
    private GenericContainer<?> messageContainer;
    
    @Override
    public void setIntegrationTestContext(DevServicesContext context) {
        this.context = context;
    }
    
    @Override
    public Map<String, String> start() {
        // Create or reuse network
        Optional<String> existingNetwork = context.containerNetworkId();
        if (existingNetwork.isPresent()) {
            // Use Dev Services network
            sharedNetwork = Network.newNetwork();
        } else {
            // Create new network
            sharedNetwork = Network.newNetwork();
        }
        
        // Start coordinated containers
        Map<String, String> config = new HashMap<>();
        config.putAll(startDatabase());
        config.putAll(startCache());
        config.putAll(startMessageBroker());
        
        return config;
    }
    
    private Map<String, String> startDatabase() {
        databaseContainer = new PostgreSQLContainer<>("postgres:13")
            .withNetwork(sharedNetwork)
            .withNetworkAliases("postgres")
            .withDatabaseName("testdb");
        
        databaseContainer.start();
        
        return Map.of(
            "quarkus.datasource.jdbc.url", databaseContainer.getJdbcUrl(),
            "quarkus.datasource.username", databaseContainer.getUsername(),
            "quarkus.datasource.password", databaseContainer.getPassword()
        );
    }
    
    private Map<String, String> startCache() {
        cacheContainer = new GenericContainer<>("redis:7")
            .withNetwork(sharedNetwork)
            .withNetworkAliases("redis")
            .withExposedPorts(6379);
        
        cacheContainer.start();
        
        return Map.of(
            "quarkus.redis.hosts", "redis://localhost:" + cacheContainer.getMappedPort(6379)
        );
    }
    
    private Map<String, String> startMessageBroker() {
        messageContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.4.0"))
            .withNetwork(sharedNetwork)
            .withNetworkAliases("kafka");
        
        messageContainer.start();
        
        return Map.of(
            "kafka.bootstrap.servers", messageContainer.getBootstrapServers()
        );
    }
    
    @Override
    public void stop() {
        if (messageContainer != null) messageContainer.stop();
        if (cacheContainer != null) cacheContainer.stop();
        if (databaseContainer != null) databaseContainer.stop();
        if (sharedNetwork != null) sharedNetwork.close();
    }
}

Test Startup Notification Interfaces

Interfaces for detecting when integration tests and native images have started, particularly useful for non-HTTP based applications.

public interface IntegrationTestStartedNotifier {
    Result check(Context context);
    
    interface Context {
        Path logFile();
    }
    
    interface Result {
        boolean isStarted();
        boolean isSsl();
        
        final class NotStarted implements Result {
            public static final NotStarted INSTANCE = new NotStarted();
            public boolean isStarted() { return false; }
            public boolean isSsl() { return false; }
        }
    }
}

@Deprecated
public interface NativeImageStartedNotifier {
    boolean isNativeImageStarted();
}

Usage Example - Custom Integration Test Notifier:

public class CustomStartupNotifier implements IntegrationTestStartedNotifier {
    @Override
    public Result check(Context context) {
        Path logFile = context.logFile();
        
        try {
            String logContent = Files.readString(logFile);
            
            // Check for application startup indicators
            if (logContent.contains("Application started successfully")) {
                // Check if SSL is enabled
                boolean sslEnabled = logContent.contains("HTTPS server started");
                return new StartedResult(sslEnabled);
            }
            
            // Check for startup failure
            if (logContent.contains("Application failed to start")) {
                throw new RuntimeException("Application startup failed");
            }
            
            // Not started yet
            return Result.NotStarted.INSTANCE;
            
        } catch (IOException e) {
            // Log file not readable yet
            return Result.NotStarted.INSTANCE;
        }
    }
    
    private static class StartedResult implements Result {
        private final boolean ssl;
        
        public StartedResult(boolean ssl) {
            this.ssl = ssl;
        }
        
        @Override
        public boolean isStarted() {
            return true;
        }
        
        @Override
        public boolean isSsl() {
            return ssl;
        }
    }
}

Service Registration:

Create META-INF/services/io.quarkus.test.common.IntegrationTestStartedNotifier:

com.mycompany.test.CustomStartupNotifier

Legacy Native Image Notifier (Deprecated):

@Deprecated
public class LegacyNativeNotifier implements NativeImageStartedNotifier {
    @Override
    public boolean isNativeImageStarted() {
        // Legacy implementation - use IntegrationTestStartedNotifier instead
        return checkApplicationProcess();
    }
    
    private boolean checkApplicationProcess() {
        // Implementation to check if native process is running
        return true;
    }
}

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