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
—
Interfaces and implementations for launching and managing different types of Quarkus artifacts during testing, including JVM, native image, and containerized deployments. The launcher system provides a unified interface for testing applications across various runtime environments.
Main interface for launching different types of Quarkus artifacts with lifecycle management and configuration support.
public interface ArtifactLauncher<T> extends Closeable {
void init(T initContext);
void start() throws IOException;
LaunchResult runToCompletion(String[] args);
void includeAsSysProps(Map<String, String> systemProps);
boolean listensOnSsl();
void close() throws IOException;
class LaunchResult {
public LaunchResult(int statusCode, byte[] output, byte[] stderror) {}
public int getStatusCode() {}
public byte[] getOutput() {}
public byte[] getStderror() {}
}
interface InitContext {
int httpPort();
int httpsPort();
Duration waitTime();
String testProfile();
List<String> argLine();
Map<String, String> env();
DevServicesLaunchResult getDevServicesLaunchResult();
}
}Basic Usage Pattern:
public class ArtifactLauncherTest {
@Test
public void testJarLauncher() throws IOException {
JarArtifactLauncher launcher = new JarArtifactLauncher();
// Initialize with context
InitContext context = new TestInitContext(8080, 8443, Duration.ofSeconds(30));
launcher.init(context);
// Configure system properties
launcher.includeAsSysProps(Map.of(
"quarkus.test.profile", "integration",
"app.environment", "test"
));
try {
// Start the application
launcher.start();
// Verify application is running
assertTrue(launcher.listensOnSsl()); // if HTTPS configured
// Run to completion with arguments
LaunchResult result = launcher.runToCompletion(new String[]{"--help"});
assertEquals(0, result.getStatusCode());
} finally {
launcher.close();
}
}
}Launches Quarkus applications packaged as executable JARs.
public class JarArtifactLauncher implements ArtifactLauncher<ArtifactLauncher.InitContext> {
// Implementation for JAR-based artifacts
}Usage Example:
@Test
public void testJarDeployment() throws IOException {
JarArtifactLauncher launcher = new JarArtifactLauncher();
InitContext context = createInitContext();
launcher.init(context);
launcher.includeAsSysProps(Map.of(
"quarkus.datasource.jdbc.url", "jdbc:h2:mem:test"
));
launcher.start();
// Test HTTP endpoints
given()
.baseUri("http://localhost:" + context.httpPort())
.when()
.get("/health")
.then()
.statusCode(200);
launcher.close();
}Launches Quarkus native images for testing native compilation scenarios.
public class NativeImageLauncher implements ArtifactLauncher<ArtifactLauncher.InitContext> {
// Implementation for native image artifacts
}Usage Example:
@Test
@EnabledIfSystemProperty(named = "native", matches = "true")
public void testNativeImage() throws IOException {
NativeImageLauncher launcher = new NativeImageLauncher();
InitContext context = createInitContext();
launcher.init(context);
launcher.start();
// Test native image startup time and memory usage
long startTime = System.currentTimeMillis();
// Verify application is responsive
given()
.baseUri("http://localhost:" + context.httpPort())
.when()
.get("/api/startup-time")
.then()
.statusCode(200)
.body("startupTime", lessThan(1000)); // Native should start quickly
launcher.close();
}Launches Quarkus applications in Docker containers.
public class DefaultDockerContainerLauncher implements ArtifactLauncher<ArtifactLauncher.InitContext> {
// Implementation for containerized artifacts
}Usage Example:
@Test
@Testcontainers
public void testContainerDeployment() throws IOException {
DefaultDockerContainerLauncher launcher = new DefaultDockerContainerLauncher();
InitContext context = createInitContext();
launcher.init(context);
launcher.includeAsSysProps(Map.of(
"quarkus.container-image.build", "true",
"quarkus.container-image.tag", "test"
));
launcher.start();
// Test containerized application
given()
.baseUri("http://localhost:" + context.httpPort())
.when()
.get("/health")
.then()
.statusCode(200)
.body("status", equalTo("UP"));
launcher.close();
}Launches applications using custom run commands.
public class RunCommandLauncher implements ArtifactLauncher<ArtifactLauncher.InitContext> {
// Implementation for custom command execution
}Utility class for common launcher operations and configuration.
public class LauncherUtil {
public static void updateConfigForPort(Path configFile, int port) {}
public static boolean isApplicationStarted(String baseUrl, Duration timeout) {}
public static void waitForApplicationStart(String baseUrl, Duration timeout) {}
// Additional utility methods
}Usage Example:
public class LauncherUtilTest {
@Test
public void testApplicationStartup() {
String baseUrl = "http://localhost:8080";
Duration timeout = Duration.ofSeconds(30);
// Wait for application to start
LauncherUtil.waitForApplicationStart(baseUrl, timeout);
// Verify application is ready
assertTrue(LauncherUtil.isApplicationStarted(baseUrl, Duration.ofSeconds(5)));
}
}Utility for reading process output streams.
public class ProcessReader {
public static void readProcessOutput(Process process, Consumer<String> outputHandler) {}
public static String captureOutput(Process process) throws IOException {}
// Additional process handling methods
}Specialized utility for detecting native image startup completion.
public class NativeImageStartedNotifier {
public static boolean waitForStartup(Process process, Duration timeout) {}
public static void notifyStartupComplete(String signal) {}
// Native image specific utilities
}Creating a custom launcher for specific deployment scenarios:
public class CustomKubernetesLauncher implements ArtifactLauncher<InitContext> {
private KubernetesClient kubernetesClient;
private String deploymentName;
@Override
public void init(InitContext initContext) {
this.kubernetesClient = new DefaultKubernetesClient();
this.deploymentName = "test-app-" + System.currentTimeMillis();
}
@Override
public void start() throws IOException {
// Deploy to Kubernetes
Deployment deployment = new DeploymentBuilder()
.withNewMetadata().withName(deploymentName).endMetadata()
.withNewSpec()
.withReplicas(1)
.withNewTemplate()
.withNewSpec()
.addNewContainer()
.withName("app")
.withImage("quarkus-app:test")
.addNewPort().withContainerPort(8080).endPort()
.endContainer()
.endSpec()
.endTemplate()
.endSpec()
.build();
kubernetesClient.apps().deployments().create(deployment);
// Wait for deployment to be ready
kubernetesClient.apps().deployments()
.withName(deploymentName)
.waitUntilReady(60, TimeUnit.SECONDS);
}
@Override
public void close() throws IOException {
if (kubernetesClient != null && deploymentName != null) {
kubernetesClient.apps().deployments().withName(deploymentName).delete();
}
}
// Implement other required methods...
}Testing across different deployment environments:
@ParameterizedTest
@ValueSource(strings = {"jar", "native", "container"})
public void testAcrossEnvironments(String launcherType) throws IOException {
ArtifactLauncher<?> launcher = createLauncher(launcherType);
InitContext context = createInitContext();
launcher.init(context);
launcher.start();
try {
// Common test logic for all environments
verifyHealthEndpoint(context.httpPort());
verifyAPIEndpoints(context.httpPort());
verifyMetrics(context.httpPort());
} finally {
launcher.close();
}
}
private ArtifactLauncher<?> createLauncher(String type) {
return switch (type) {
case "jar" -> new JarArtifactLauncher();
case "native" -> new NativeImageLauncher();
case "container" -> new DefaultDockerContainerLauncher();
default -> throw new IllegalArgumentException("Unknown launcher type: " + type);
};
}Measuring application performance across different deployment modes:
public class PerformanceTest {
@Test
public void compareStartupTimes() throws IOException {
Map<String, Duration> startupTimes = new HashMap<>();
// Test JAR startup
startupTimes.put("JAR", measureStartupTime(new JarArtifactLauncher()));
// Test native image startup
startupTimes.put("Native", measureStartupTime(new NativeImageLauncher()));
// Verify native is faster
assertTrue(startupTimes.get("Native").compareTo(startupTimes.get("JAR")) < 0,
"Native image should start faster than JAR");
System.out.println("Startup times: " + startupTimes);
}
private Duration measureStartupTime(ArtifactLauncher<?> launcher) throws IOException {
InitContext context = createInitContext();
launcher.init(context);
long start = System.currentTimeMillis();
launcher.start();
// Wait for application to be responsive
LauncherUtil.waitForApplicationStart(
"http://localhost:" + context.httpPort(),
Duration.ofSeconds(60)
);
long end = System.currentTimeMillis();
launcher.close();
return Duration.ofMillis(end - start);
}
}Monitoring resource usage during testing:
public class ResourceMonitoringTest {
@Test
public void monitorResourceUsage() throws IOException {
JarArtifactLauncher launcher = new JarArtifactLauncher();
InitContext context = createInitContext();
launcher.init(context);
launcher.start();
try {
// Monitor memory usage
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
long initialMemory = memoryBean.getHeapMemoryUsage().getUsed();
// Simulate load
IntStream.range(0, 1000).parallel().forEach(i -> {
given()
.baseUri("http://localhost:" + context.httpPort())
.when()
.get("/api/data/" + i)
.then()
.statusCode(200);
});
long finalMemory = memoryBean.getHeapMemoryUsage().getUsed();
long memoryIncrease = finalMemory - initialMemory;
System.out.println("Memory increase: " + memoryIncrease + " bytes");
assertTrue(memoryIncrease < 100_000_000, "Memory usage should be reasonable");
} finally {
launcher.close();
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-test-common