0
# Test Context and Status
1
2
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.
3
4
## Capabilities
5
6
### TestStatus Class
7
8
Represents test execution status with access to failure information for conditional resource cleanup and debugging.
9
10
```java { .api }
11
public class TestStatus {
12
public TestStatus(Throwable testErrorCause) {}
13
public Throwable getTestErrorCause() {}
14
public boolean isTestFailed() {}
15
}
16
```
17
18
**Usage in Test Resources:**
19
```java
20
public class LoggingTestResource implements QuarkusTestResourceLifecycleManager {
21
private TestStatus testStatus;
22
23
@Override
24
public void setContext(Context context) {
25
this.testStatus = context.getTestStatus();
26
}
27
28
@Override
29
public void stop() {
30
if (testStatus.isTestFailed()) {
31
// Capture additional logs for failed tests
32
captureFailureLogs();
33
34
Throwable cause = testStatus.getTestErrorCause();
35
if (cause != null) {
36
System.err.println("Test failed with: " + cause.getMessage());
37
cause.printStackTrace();
38
}
39
}
40
}
41
42
private void captureFailureLogs() {
43
// Implementation to capture logs, database state, etc.
44
System.out.println("Capturing failure diagnostics...");
45
}
46
}
47
```
48
49
### DevServicesContext Interface
50
51
Provides access to Dev Services properties and container networking information for test resource coordination.
52
53
```java { .api }
54
public interface DevServicesContext {
55
Map<String, String> devServicesProperties();
56
Optional<String> containerNetworkId();
57
58
interface ContextAware {
59
void setIntegrationTestContext(DevServicesContext context);
60
}
61
}
62
```
63
64
**Usage Example:**
65
```java
66
public class DatabaseAwareTestResource implements QuarkusTestResourceLifecycleManager,
67
DevServicesContext.ContextAware {
68
private DevServicesContext devServicesContext;
69
70
@Override
71
public void setIntegrationTestContext(DevServicesContext context) {
72
this.devServicesContext = context;
73
}
74
75
@Override
76
public Map<String, String> start() {
77
// Access Dev Services database properties
78
Map<String, String> devServicesProps = devServicesContext.devServicesProperties();
79
String dbUrl = devServicesProps.get("quarkus.datasource.jdbc.url");
80
81
if (dbUrl != null) {
82
System.out.println("Using Dev Services database: " + dbUrl);
83
84
// Use the same network for additional containers
85
Optional<String> networkId = devServicesContext.containerNetworkId();
86
if (networkId.isPresent()) {
87
return configureWithNetwork(networkId.get());
88
}
89
}
90
91
return Map.of();
92
}
93
94
private Map<String, String> configureWithNetwork(String networkId) {
95
// Configure additional test containers to use the same network
96
// This allows containers to communicate with Dev Services
97
GenericContainer<?> additionalService = new GenericContainer<>("redis:7")
98
.withNetwork(Network.newNetwork())
99
.withExposedPorts(6379);
100
101
additionalService.start();
102
103
return Map.of(
104
"quarkus.redis.hosts", "redis://localhost:" + additionalService.getMappedPort(6379)
105
);
106
}
107
}
108
```
109
110
### ListeningAddress Class
111
112
Represents server listening address information for test URL construction and service discovery.
113
114
```java { .api }
115
public class ListeningAddress {
116
public ListeningAddress(Integer port, String protocol) {}
117
public Integer getPort() {}
118
public String getProtocol() {}
119
public boolean isSsl() {}
120
}
121
```
122
123
**Usage Example:**
124
```java
125
public class ServiceDiscoveryTest {
126
@Test
127
public void testServiceEndpoints() {
128
// Get application listening addresses
129
ListeningAddress httpAddress = new ListeningAddress(8080, "http");
130
ListeningAddress httpsAddress = new ListeningAddress(8443, "https");
131
132
// Test HTTP endpoint
133
assertFalse(httpAddress.isSsl());
134
assertEquals("http", httpAddress.getProtocol());
135
136
String httpUrl = httpAddress.getProtocol() + "://localhost:" + httpAddress.getPort();
137
given()
138
.baseUri(httpUrl)
139
.when()
140
.get("/health")
141
.then()
142
.statusCode(200);
143
144
// Test HTTPS endpoint
145
assertTrue(httpsAddress.isSsl());
146
assertEquals("https", httpsAddress.getProtocol());
147
148
String httpsUrl = httpsAddress.getProtocol() + "://localhost:" + httpsAddress.getPort();
149
given()
150
.baseUri(httpsUrl)
151
.relaxedHTTPSValidation()
152
.when()
153
.get("/health")
154
.then()
155
.statusCode(200);
156
}
157
}
158
```
159
160
## Advanced Usage Patterns
161
162
### Conditional Test Resource Behavior
163
164
Using test status for conditional resource management:
165
166
```java
167
public class ConditionalCleanupResource implements QuarkusTestResourceLifecycleManager {
168
private ExternalService externalService;
169
private TestStatus testStatus;
170
171
@Override
172
public void setContext(Context context) {
173
this.testStatus = context.getTestStatus();
174
}
175
176
@Override
177
public Map<String, String> start() {
178
externalService = new ExternalService();
179
externalService.start();
180
181
return Map.of(
182
"external.service.url", externalService.getUrl()
183
);
184
}
185
186
@Override
187
public void stop() {
188
if (testStatus.isTestFailed()) {
189
// Preserve resources for debugging
190
System.out.println("Test failed - preserving external service for debugging");
191
System.out.println("Service URL: " + externalService.getUrl());
192
System.out.println("Admin URL: " + externalService.getAdminUrl());
193
194
// Optionally create debug snapshot
195
externalService.createDebugSnapshot();
196
} else {
197
// Normal cleanup
198
externalService.stop();
199
}
200
}
201
}
202
```
203
204
### Dev Services Integration
205
206
Integrating with existing Dev Services containers:
207
208
```java
209
public class DevServicesIntegrationResource implements QuarkusTestResourceLifecycleManager,
210
DevServicesContext.ContextAware {
211
private DevServicesContext context;
212
private GenericContainer<?> additionalContainer;
213
214
@Override
215
public void setIntegrationTestContext(DevServicesContext context) {
216
this.context = context;
217
}
218
219
@Override
220
public Map<String, String> start() {
221
Map<String, String> config = new HashMap<>();
222
223
// Get Dev Services properties
224
Map<String, String> devProps = context.devServicesProperties();
225
226
// Check if Dev Services PostgreSQL is running
227
String dbUrl = devProps.get("quarkus.datasource.jdbc.url");
228
if (dbUrl != null && dbUrl.contains("postgresql")) {
229
// Start additional container in same network
230
Optional<String> networkId = context.containerNetworkId();
231
232
if (networkId.isPresent()) {
233
additionalContainer = new GenericContainer<>("adminer:4")
234
.withNetwork(Network.newNetwork())
235
.withExposedPorts(8080)
236
.withEnv("ADMINER_DEFAULT_SERVER", extractHostFromUrl(dbUrl));
237
238
additionalContainer.start();
239
240
config.put("adminer.url",
241
"http://localhost:" + additionalContainer.getMappedPort(8080));
242
}
243
}
244
245
return config;
246
}
247
248
@Override
249
public void stop() {
250
if (additionalContainer != null) {
251
additionalContainer.stop();
252
}
253
}
254
255
private String extractHostFromUrl(String jdbcUrl) {
256
// Extract hostname from JDBC URL for container networking
257
return jdbcUrl.replaceAll(".*//([^:/]+).*", "$1");
258
}
259
}
260
```
261
262
### Test Profile Context
263
264
Using test profile information for conditional behavior:
265
266
```java
267
public class ProfileAwareResource implements QuarkusTestResourceLifecycleManager {
268
private String testProfile;
269
270
@Override
271
public void setContext(Context context) {
272
this.testProfile = context.testProfile();
273
}
274
275
@Override
276
public Map<String, String> start() {
277
Map<String, String> config = new HashMap<>();
278
279
switch (testProfile) {
280
case "integration":
281
// Start full external services
282
config.putAll(startIntegrationServices());
283
break;
284
285
case "performance":
286
// Start monitoring and metrics services
287
config.putAll(startPerformanceServices());
288
break;
289
290
case "security":
291
// Start security test services
292
config.putAll(startSecurityServices());
293
break;
294
295
default:
296
// Minimal setup for unit tests
297
config.putAll(startMinimalServices());
298
}
299
300
return config;
301
}
302
303
private Map<String, String> startIntegrationServices() {
304
// Start database, message broker, external APIs
305
return Map.of(
306
"postgres.url", startPostgres(),
307
"kafka.url", startKafka(),
308
"external.api.url", startMockAPI()
309
);
310
}
311
312
private Map<String, String> startPerformanceServices() {
313
// Start monitoring stack
314
return Map.of(
315
"prometheus.url", startPrometheus(),
316
"grafana.url", startGrafana()
317
);
318
}
319
320
// Additional profile-specific configurations...
321
}
322
```
323
324
### Multi-Container Coordination
325
326
Coordinating multiple containers with shared networking:
327
328
```java
329
public class MultiContainerResource implements QuarkusTestResourceLifecycleManager,
330
DevServicesContext.ContextAware {
331
private DevServicesContext context;
332
private Network sharedNetwork;
333
private GenericContainer<?> databaseContainer;
334
private GenericContainer<?> cacheContainer;
335
private GenericContainer<?> messageContainer;
336
337
@Override
338
public void setIntegrationTestContext(DevServicesContext context) {
339
this.context = context;
340
}
341
342
@Override
343
public Map<String, String> start() {
344
// Create or reuse network
345
Optional<String> existingNetwork = context.containerNetworkId();
346
if (existingNetwork.isPresent()) {
347
// Use Dev Services network
348
sharedNetwork = Network.newNetwork();
349
} else {
350
// Create new network
351
sharedNetwork = Network.newNetwork();
352
}
353
354
// Start coordinated containers
355
Map<String, String> config = new HashMap<>();
356
config.putAll(startDatabase());
357
config.putAll(startCache());
358
config.putAll(startMessageBroker());
359
360
return config;
361
}
362
363
private Map<String, String> startDatabase() {
364
databaseContainer = new PostgreSQLContainer<>("postgres:13")
365
.withNetwork(sharedNetwork)
366
.withNetworkAliases("postgres")
367
.withDatabaseName("testdb");
368
369
databaseContainer.start();
370
371
return Map.of(
372
"quarkus.datasource.jdbc.url", databaseContainer.getJdbcUrl(),
373
"quarkus.datasource.username", databaseContainer.getUsername(),
374
"quarkus.datasource.password", databaseContainer.getPassword()
375
);
376
}
377
378
private Map<String, String> startCache() {
379
cacheContainer = new GenericContainer<>("redis:7")
380
.withNetwork(sharedNetwork)
381
.withNetworkAliases("redis")
382
.withExposedPorts(6379);
383
384
cacheContainer.start();
385
386
return Map.of(
387
"quarkus.redis.hosts", "redis://localhost:" + cacheContainer.getMappedPort(6379)
388
);
389
}
390
391
private Map<String, String> startMessageBroker() {
392
messageContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.4.0"))
393
.withNetwork(sharedNetwork)
394
.withNetworkAliases("kafka");
395
396
messageContainer.start();
397
398
return Map.of(
399
"kafka.bootstrap.servers", messageContainer.getBootstrapServers()
400
);
401
}
402
403
@Override
404
public void stop() {
405
if (messageContainer != null) messageContainer.stop();
406
if (cacheContainer != null) cacheContainer.stop();
407
if (databaseContainer != null) databaseContainer.stop();
408
if (sharedNetwork != null) sharedNetwork.close();
409
}
410
}
411
```
412
413
### Test Startup Notification Interfaces
414
415
Interfaces for detecting when integration tests and native images have started, particularly useful for non-HTTP based applications.
416
417
```java { .api }
418
public interface IntegrationTestStartedNotifier {
419
Result check(Context context);
420
421
interface Context {
422
Path logFile();
423
}
424
425
interface Result {
426
boolean isStarted();
427
boolean isSsl();
428
429
final class NotStarted implements Result {
430
public static final NotStarted INSTANCE = new NotStarted();
431
public boolean isStarted() { return false; }
432
public boolean isSsl() { return false; }
433
}
434
}
435
}
436
437
@Deprecated
438
public interface NativeImageStartedNotifier {
439
boolean isNativeImageStarted();
440
}
441
```
442
443
**Usage Example - Custom Integration Test Notifier:**
444
445
```java
446
public class CustomStartupNotifier implements IntegrationTestStartedNotifier {
447
@Override
448
public Result check(Context context) {
449
Path logFile = context.logFile();
450
451
try {
452
String logContent = Files.readString(logFile);
453
454
// Check for application startup indicators
455
if (logContent.contains("Application started successfully")) {
456
// Check if SSL is enabled
457
boolean sslEnabled = logContent.contains("HTTPS server started");
458
return new StartedResult(sslEnabled);
459
}
460
461
// Check for startup failure
462
if (logContent.contains("Application failed to start")) {
463
throw new RuntimeException("Application startup failed");
464
}
465
466
// Not started yet
467
return Result.NotStarted.INSTANCE;
468
469
} catch (IOException e) {
470
// Log file not readable yet
471
return Result.NotStarted.INSTANCE;
472
}
473
}
474
475
private static class StartedResult implements Result {
476
private final boolean ssl;
477
478
public StartedResult(boolean ssl) {
479
this.ssl = ssl;
480
}
481
482
@Override
483
public boolean isStarted() {
484
return true;
485
}
486
487
@Override
488
public boolean isSsl() {
489
return ssl;
490
}
491
}
492
}
493
```
494
495
**Service Registration:**
496
497
Create `META-INF/services/io.quarkus.test.common.IntegrationTestStartedNotifier`:
498
499
```
500
com.mycompany.test.CustomStartupNotifier
501
```
502
503
**Legacy Native Image Notifier (Deprecated):**
504
505
```java
506
@Deprecated
507
public class LegacyNativeNotifier implements NativeImageStartedNotifier {
508
@Override
509
public boolean isNativeImageStarted() {
510
// Legacy implementation - use IntegrationTestStartedNotifier instead
511
return checkApplicationProcess();
512
}
513
514
private boolean checkApplicationProcess() {
515
// Implementation to check if native process is running
516
return true;
517
}
518
}
519
```