0
# Test Callbacks
1
2
Quarkus JUnit 5 provides a comprehensive callback system through service provider interfaces that allow customization of test execution lifecycle at various phases.
3
4
## Callback Interfaces
5
6
### QuarkusTestBeforeEachCallback
7
8
Called before each test method in a `@QuarkusTest`.
9
10
```java { .api }
11
public interface QuarkusTestBeforeEachCallback {
12
void beforeEach(QuarkusTestMethodContext context);
13
}
14
```
15
16
### QuarkusTestAfterEachCallback
17
18
Called after each test method in a `@QuarkusTest`.
19
20
```java { .api }
21
public interface QuarkusTestAfterEachCallback {
22
void afterEach(QuarkusTestMethodContext context);
23
}
24
```
25
26
### QuarkusTestBeforeClassCallback
27
28
Called before the test class is executed.
29
30
```java { .api }
31
public interface QuarkusTestBeforeClassCallback {
32
void beforeClass(Class<?> testClass);
33
}
34
```
35
36
### QuarkusTestAfterAllCallback
37
38
Called after all tests in a test class have been executed.
39
40
```java { .api }
41
public interface QuarkusTestAfterAllCallback {
42
void afterAll(QuarkusTestContext context);
43
}
44
```
45
46
### QuarkusTestAfterConstructCallback
47
48
Called after the test instance has been constructed.
49
50
```java { .api }
51
public interface QuarkusTestAfterConstructCallback {
52
void afterConstruct(Object testInstance);
53
}
54
```
55
56
### QuarkusTestBeforeTestExecutionCallback
57
58
Called immediately before test method execution.
59
60
```java { .api }
61
public interface QuarkusTestBeforeTestExecutionCallback {
62
void beforeTestExecution(QuarkusTestMethodContext context);
63
}
64
```
65
66
### QuarkusTestAfterTestExecutionCallback
67
68
Called immediately after test method execution.
69
70
```java { .api }
71
public interface QuarkusTestAfterTestExecutionCallback {
72
void afterTestExecution(QuarkusTestMethodContext context);
73
}
74
```
75
76
## Context Classes
77
78
### QuarkusTestContext
79
80
Context object passed to test callbacks containing test execution information.
81
82
```java { .api }
83
public class QuarkusTestContext {
84
85
/**
86
* Get the test instance
87
*/
88
public Object getTestInstance();
89
90
/**
91
* Get outer test instances (for nested tests)
92
*/
93
public List<Object> getOuterInstances();
94
95
/**
96
* Get the test status including failure information
97
*/
98
public TestStatus getTestStatus();
99
}
100
```
101
102
### QuarkusTestMethodContext
103
104
Extended context for method-level callbacks, providing access to the test method.
105
106
```java { .api }
107
public final class QuarkusTestMethodContext extends QuarkusTestContext {
108
109
/**
110
* Get the test method being executed
111
*/
112
public Method getTestMethod();
113
}
114
```
115
116
## Service Provider Registration
117
118
Callback implementations must be registered as service providers in `META-INF/services/` files.
119
120
### Example Service Registration
121
122
Create file: `META-INF/services/io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback`
123
124
```text
125
com.example.MyTestSetupCallback
126
com.example.DatabaseCleanupCallback
127
```
128
129
## Callback Implementation Examples
130
131
### Database Cleanup Callback
132
133
```java
134
import io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback;
135
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
136
import jakarta.enterprise.inject.spi.CDI;
137
import jakarta.persistence.EntityManager;
138
import jakarta.transaction.Transactional;
139
140
public class DatabaseCleanupCallback implements QuarkusTestAfterEachCallback {
141
142
@Override
143
public void afterEach(QuarkusTestMethodContext context) {
144
// Clean up database after each test
145
EntityManager em = CDI.current().select(EntityManager.class).get();
146
147
cleanupTestData(em);
148
}
149
150
@Transactional
151
private void cleanupTestData(EntityManager em) {
152
em.createQuery("DELETE FROM TestEntity").executeUpdate();
153
em.createQuery("DELETE FROM UserEntity WHERE email LIKE '%test%'").executeUpdate();
154
}
155
}
156
```
157
158
### Test Metrics Collector
159
160
```java
161
import io.quarkus.test.junit.callback.QuarkusTestBeforeTestExecutionCallback;
162
import io.quarkus.test.junit.callback.QuarkusTestAfterTestExecutionCallback;
163
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
164
import java.util.concurrent.ConcurrentHashMap;
165
import java.util.Map;
166
167
public class TestMetricsCallback implements
168
QuarkusTestBeforeTestExecutionCallback,
169
QuarkusTestAfterTestExecutionCallback {
170
171
private final Map<String, Long> testStartTimes = new ConcurrentHashMap<>();
172
private final Map<String, Long> testDurations = new ConcurrentHashMap<>();
173
174
@Override
175
public void beforeTestExecution(QuarkusTestMethodContext context) {
176
String testKey = getTestKey(context);
177
testStartTimes.put(testKey, System.currentTimeMillis());
178
System.out.println("Starting test: " + testKey);
179
}
180
181
@Override
182
public void afterTestExecution(QuarkusTestMethodContext context) {
183
String testKey = getTestKey(context);
184
Long startTime = testStartTimes.remove(testKey);
185
186
if (startTime != null) {
187
long duration = System.currentTimeMillis() - startTime;
188
testDurations.put(testKey, duration);
189
System.out.println("Test " + testKey + " took " + duration + "ms");
190
}
191
}
192
193
private String getTestKey(QuarkusTestMethodContext context) {
194
return context.getTestInstance().getClass().getSimpleName() +
195
"." + context.getTestMethod().getName();
196
}
197
}
198
```
199
200
### Mock Installation Callback
201
202
```java
203
import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
204
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
205
import io.quarkus.test.junit.QuarkusMock;
206
import org.mockito.Mockito;
207
import java.lang.reflect.Field;
208
209
public class AutoMockCallback implements QuarkusTestBeforeEachCallback {
210
211
@Override
212
public void beforeEach(QuarkusTestMethodContext context) {
213
Object testInstance = context.getTestInstance();
214
Class<?> testClass = testInstance.getClass();
215
216
// Find fields annotated with custom @MockBean annotation
217
for (Field field : testClass.getDeclaredFields()) {
218
if (field.isAnnotationPresent(MockBean.class)) {
219
installMockForField(field, testInstance);
220
}
221
}
222
}
223
224
private void installMockForField(Field field, Object testInstance) {
225
try {
226
field.setAccessible(true);
227
Object realBean = field.get(testInstance);
228
229
if (realBean != null) {
230
Object mock = Mockito.mock(field.getType());
231
QuarkusMock.installMockForInstance(mock, realBean);
232
233
// Replace field value with mock for easy access in tests
234
field.set(testInstance, mock);
235
}
236
} catch (Exception e) {
237
throw new RuntimeException("Failed to install mock for field: " + field.getName(), e);
238
}
239
}
240
}
241
242
// Custom annotation
243
@Retention(RetentionPolicy.RUNTIME)
244
@Target(ElementType.FIELD)
245
public @interface MockBean {
246
}
247
```
248
249
### Test Data Setup Callback
250
251
```java
252
import io.quarkus.test.junit.callback.QuarkusTestAfterConstructCallback;
253
import io.quarkus.test.junit.callback.QuarkusTestBeforeClassCallback;
254
import jakarta.enterprise.inject.spi.CDI;
255
import jakarta.persistence.EntityManager;
256
import jakarta.transaction.Transactional;
257
258
public class TestDataSetupCallback implements
259
QuarkusTestAfterConstructCallback,
260
QuarkusTestBeforeClassCallback {
261
262
@Override
263
public void beforeClass(Class<?> testClass) {
264
if (testClass.isAnnotationPresent(WithTestData.class)) {
265
setupTestData(testClass.getAnnotation(WithTestData.class));
266
}
267
}
268
269
@Override
270
public void afterConstruct(Object testInstance) {
271
Class<?> testClass = testInstance.getClass();
272
if (testClass.isAnnotationPresent(WithMethodTestData.class)) {
273
setupMethodSpecificData(testInstance);
274
}
275
}
276
277
@Transactional
278
void setupTestData(WithTestData annotation) {
279
EntityManager em = CDI.current().select(EntityManager.class).get();
280
281
for (String dataset : annotation.datasets()) {
282
loadDataset(em, dataset);
283
}
284
}
285
286
private void loadDataset(EntityManager em, String dataset) {
287
switch (dataset) {
288
case "users":
289
em.persist(new User("test1@example.com", "Test User 1"));
290
em.persist(new User("test2@example.com", "Test User 2"));
291
break;
292
case "products":
293
em.persist(new Product("Laptop", 999.99));
294
em.persist(new Product("Mouse", 29.99));
295
break;
296
}
297
}
298
299
private void setupMethodSpecificData(Object testInstance) {
300
// Setup data specific to individual test methods
301
}
302
}
303
304
// Custom annotations
305
@Retention(RetentionPolicy.RUNTIME)
306
@Target(ElementType.TYPE)
307
public @interface WithTestData {
308
String[] datasets() default {};
309
}
310
311
@Retention(RetentionPolicy.RUNTIME)
312
@Target(ElementType.TYPE)
313
public @interface WithMethodTestData {
314
String value() default "";
315
}
316
```
317
318
## Error Handling in Callbacks
319
320
### Safe Callback Implementation
321
322
```java
323
import io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback;
324
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
325
import org.slf4j.Logger;
326
import org.slf4j.LoggerFactory;
327
328
public class SafeCleanupCallback implements QuarkusTestAfterEachCallback {
329
330
private static final Logger logger = LoggerFactory.getLogger(SafeCleanupCallback.class);
331
332
@Override
333
public void afterEach(QuarkusTestMethodContext context) {
334
try {
335
performCleanup(context);
336
} catch (Exception e) {
337
// Log error but don't fail the test
338
logger.error("Cleanup failed for test: " +
339
context.getTestMethod().getName(), e);
340
}
341
}
342
343
private void performCleanup(QuarkusTestMethodContext context) {
344
// Cleanup logic that might fail
345
TestStatus status = context.getTestStatus();
346
if (status.getTestErrorCause() != null) {
347
// Special cleanup for failed tests
348
logger.info("Test failed, performing error cleanup");
349
}
350
}
351
}
352
```
353
354
### Context Information Usage
355
356
```java
357
import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
358
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
359
import java.lang.reflect.Method;
360
361
public class ContextAwareCallback implements QuarkusTestBeforeEachCallback {
362
363
@Override
364
public void beforeEach(QuarkusTestMethodContext context) {
365
Method testMethod = context.getTestMethod();
366
Object testInstance = context.getTestInstance();
367
368
// Check for custom test annotations
369
if (testMethod.isAnnotationPresent(SlowTest.class)) {
370
System.out.println("Starting slow test: " + testMethod.getName());
371
// Maybe increase timeout or adjust configuration
372
}
373
374
// Access test class information
375
Class<?> testClass = testInstance.getClass();
376
if (testClass.isAnnotationPresent(DatabaseTest.class)) {
377
// Setup database-specific configuration
378
setupDatabaseForTest();
379
}
380
381
// Check outer instances for nested tests
382
if (!context.getOuterInstances().isEmpty()) {
383
System.out.println("Running nested test in: " +
384
context.getOuterInstances().get(0).getClass().getSimpleName());
385
}
386
}
387
388
private void setupDatabaseForTest() {
389
// Database setup logic
390
}
391
}
392
393
@Retention(RetentionPolicy.RUNTIME)
394
@Target(ElementType.METHOD)
395
@interface SlowTest {
396
}
397
398
@Retention(RetentionPolicy.RUNTIME)
399
@Target(ElementType.TYPE)
400
@interface DatabaseTest {
401
}
402
```
403
404
## Advanced Callback Patterns
405
406
### Conditional Callback Execution
407
408
```java
409
import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
410
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
411
412
public class ConditionalCallback implements QuarkusTestBeforeEachCallback {
413
414
@Override
415
public void beforeEach(QuarkusTestMethodContext context) {
416
// Only execute for certain test classes
417
if (shouldExecuteForTest(context)) {
418
performSetup(context);
419
}
420
}
421
422
private boolean shouldExecuteForTest(QuarkusTestMethodContext context) {
423
Class<?> testClass = context.getTestInstance().getClass();
424
425
// Execute only for integration tests
426
return testClass.getPackage().getName().contains("integration");
427
}
428
429
private void performSetup(QuarkusTestMethodContext context) {
430
// Setup logic
431
}
432
}
433
```
434
435
### Resource Management Callback
436
437
```java
438
import io.quarkus.test.junit.callback.QuarkusTestBeforeClassCallback;
439
import io.quarkus.test.junit.callback.QuarkusTestAfterAllCallback;
440
import io.quarkus.test.junit.callback.QuarkusTestContext;
441
import java.util.concurrent.ConcurrentHashMap;
442
import java.util.Map;
443
444
public class ResourceManagerCallback implements
445
QuarkusTestBeforeClassCallback,
446
QuarkusTestAfterAllCallback {
447
448
private final Map<Class<?>, AutoCloseable> classResources = new ConcurrentHashMap<>();
449
450
@Override
451
public void beforeClass(Class<?> testClass) {
452
if (testClass.isAnnotationPresent(RequiresExternalService.class)) {
453
try {
454
ExternalServiceMock service = new ExternalServiceMock();
455
service.start();
456
classResources.put(testClass, service);
457
} catch (Exception e) {
458
throw new RuntimeException("Failed to start external service", e);
459
}
460
}
461
}
462
463
@Override
464
public void afterAll(QuarkusTestContext context) {
465
Class<?> testClass = context.getTestInstance().getClass();
466
AutoCloseable resource = classResources.remove(testClass);
467
468
if (resource != null) {
469
try {
470
resource.close();
471
} catch (Exception e) {
472
System.err.println("Failed to close resource: " + e.getMessage());
473
}
474
}
475
}
476
}
477
478
@Retention(RetentionPolicy.RUNTIME)
479
@Target(ElementType.TYPE)
480
@interface RequiresExternalService {
481
}
482
```
483
484
## Best Practices
485
486
### Service Provider Declaration
487
488
Always declare your callback implementations in the appropriate service files:
489
490
```text
491
# META-INF/services/io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback
492
com.example.DatabaseCleanupCallback
493
com.example.TestMetricsCallback
494
495
# META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterConstructCallback
496
com.example.AutoMockCallback
497
com.example.TestDataSetupCallback
498
```
499
500
### Error Handling
501
502
```java
503
public class RobustCallback implements QuarkusTestAfterEachCallback {
504
505
@Override
506
public void afterEach(QuarkusTestMethodContext context) {
507
try {
508
// Callback logic
509
performCleanup();
510
} catch (Exception e) {
511
// Never let callback exceptions fail tests
512
logError("Callback failed", e);
513
}
514
}
515
516
private void logError(String message, Exception e) {
517
// Use appropriate logging framework
518
System.err.println(message + ": " + e.getMessage());
519
}
520
}
521
```
522
523
### Performance Considerations
524
525
```java
526
public class EfficientCallback implements QuarkusTestBeforeEachCallback {
527
528
// Cache expensive resources
529
private static final Map<String, Object> cache = new ConcurrentHashMap<>();
530
531
@Override
532
public void beforeEach(QuarkusTestMethodContext context) {
533
String cacheKey = context.getTestMethod().getName();
534
535
// Use cached resources when possible
536
Object resource = cache.computeIfAbsent(cacheKey, this::createResource);
537
538
// Apply resource to test
539
applyResource(context, resource);
540
}
541
542
private Object createResource(String key) {
543
// Create expensive resource only once per test method
544
return new ExpensiveResource();
545
}
546
547
private void applyResource(QuarkusTestMethodContext context, Object resource) {
548
// Apply resource to test execution
549
}
550
}
551
```