0
# Build Chain Customization
1
2
Support for customizing Quarkus build chains during testing, including build steps, producers, and consumers for comprehensive build testing.
3
4
## ProdModeTestBuildStep
5
6
Abstract base class for creating custom build steps in production mode tests.
7
8
### Core API
9
10
```java { .api }
11
public abstract class ProdModeTestBuildStep implements BuildStep {
12
13
public ProdModeTestBuildStep(Map<String, Object> testContext);
14
public Map<String, Object> getTestContext();
15
}
16
```
17
18
## ProdModeTestBuildChainBuilderConsumer
19
20
Consumer for customizing build chains in production mode tests.
21
22
### Core API
23
24
```java { .api }
25
public class ProdModeTestBuildChainBuilderConsumer implements Consumer<BuildChainBuilder> {
26
27
public ProdModeTestBuildChainBuilderConsumer(String buildStepClassName,
28
List<String> producesClassNames, List<String> consumesClassNames,
29
Map<String, Object> testContext);
30
31
public void accept(BuildChainBuilder builder);
32
}
33
```
34
35
## ProdModeTestBuildChainCustomizerProducer
36
37
Function for producing build chain customizers.
38
39
### Core API
40
41
```java { .api }
42
public class ProdModeTestBuildChainCustomizerProducer implements Function<Map<String, Object>, List<Consumer<BuildChainBuilder>>> {
43
44
public List<Consumer<BuildChainBuilder>> apply(Map<String, Object> testContext);
45
}
46
```
47
48
## Usage Examples
49
50
### Creating Custom Build Steps
51
52
```java
53
import io.quarkus.test.ProdModeTestBuildStep;
54
import io.quarkus.deployment.builditem.BuildItem;
55
import io.quarkus.deployment.BuildContext;
56
57
public class MyCustomBuildStep extends ProdModeTestBuildStep {
58
59
public MyCustomBuildStep(Map<String, Object> testContext) {
60
super(testContext);
61
}
62
63
@Override
64
public void execute(BuildContext context) {
65
// Access test context
66
Map<String, Object> testContext = getTestContext();
67
String customValue = (String) testContext.get("customKey");
68
69
// Perform custom build logic
70
MyBuildItem buildItem = new MyBuildItem(customValue);
71
context.produce(buildItem);
72
}
73
}
74
75
// Custom build item
76
public class MyBuildItem extends BuildItem {
77
private final String value;
78
79
public MyBuildItem(String value) {
80
this.value = value;
81
}
82
83
public String getValue() {
84
return value;
85
}
86
}
87
```
88
89
### Using Build Steps in Tests
90
91
```java
92
import io.quarkus.test.QuarkusProdModeTest;
93
import org.junit.jupiter.api.extension.RegisterExtension;
94
import org.junit.jupiter.api.Test;
95
96
public class BuildChainCustomizationTest {
97
98
@RegisterExtension
99
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
100
.withApplicationRoot(jar -> jar.addClasses(MyService.class))
101
.addBuildChainCustomizerEntries(new QuarkusProdModeTest.BuildChainCustomizerEntry(
102
MyCustomBuildStep.class,
103
List.of(MyBuildItem.class), // Produces
104
List.of(ApplicationArchivesBuildItem.class) // Consumes
105
));
106
107
@Test
108
public void testCustomBuildStep() {
109
// Test that custom build step was executed
110
// Build artifacts will include MyBuildItem
111
}
112
}
113
```
114
115
### Complex Build Step with Dependencies
116
117
```java
118
public class ComplexBuildStep extends ProdModeTestBuildStep {
119
120
public ComplexBuildStep(Map<String, Object> testContext) {
121
super(testContext);
122
}
123
124
@Override
125
public void execute(BuildContext context) {
126
// Consume required build items
127
ApplicationArchivesBuildItem archives = context.consume(ApplicationArchivesBuildItem.class);
128
FeatureBuildItem features = context.consume(FeatureBuildItem.class);
129
130
// Access test context for configuration
131
Map<String, Object> testContext = getTestContext();
132
boolean enableFeature = (Boolean) testContext.getOrDefault("enableFeature", false);
133
134
if (enableFeature) {
135
// Produce custom build items
136
context.produce(new CustomFeatureBuildItem());
137
context.produce(new AdditionalBeanBuildItem(MyCustomBean.class));
138
}
139
140
// Modify existing build items if needed
141
for (ApplicationArchive archive : archives.getAllApplicationArchives()) {
142
// Process archive
143
}
144
}
145
}
146
```
147
148
### Multi-Step Build Chain
149
150
```java
151
public class FirstBuildStep extends ProdModeTestBuildStep {
152
public FirstBuildStep(Map<String, Object> testContext) {
153
super(testContext);
154
}
155
156
@Override
157
public void execute(BuildContext context) {
158
context.produce(new IntermediateBuildItem("processed-data"));
159
}
160
}
161
162
public class SecondBuildStep extends ProdModeTestBuildStep {
163
public SecondBuildStep(Map<String, Object> testContext) {
164
super(testContext);
165
}
166
167
@Override
168
public void execute(BuildContext context) {
169
IntermediateBuildItem intermediate = context.consume(IntermediateBuildItem.class);
170
String processedData = intermediate.getData();
171
172
// Final processing
173
context.produce(new FinalBuildItem(processedData + "-final"));
174
}
175
}
176
177
// Test with multiple build steps
178
@RegisterExtension
179
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
180
.withApplicationRoot(jar -> jar.addClasses(MyService.class))
181
.addBuildChainCustomizerEntries(new QuarkusProdModeTest.BuildChainCustomizerEntry(
182
FirstBuildStep.class,
183
List.of(IntermediateBuildItem.class),
184
List.of()
185
))
186
.addBuildChainCustomizerEntries(new QuarkusProdModeTest.BuildChainCustomizerEntry(
187
SecondBuildStep.class,
188
List.of(FinalBuildItem.class),
189
List.of(IntermediateBuildItem.class)
190
));
191
```
192
193
### Conditional Build Steps
194
195
```java
196
public class ConditionalBuildStep extends ProdModeTestBuildStep {
197
198
public ConditionalBuildStep(Map<String, Object> testContext) {
199
super(testContext);
200
}
201
202
@Override
203
public void execute(BuildContext context) {
204
Map<String, Object> testContext = getTestContext();
205
206
// Check conditions from test context
207
String environment = (String) testContext.get("environment");
208
boolean debugMode = (Boolean) testContext.getOrDefault("debug", false);
209
210
if ("production".equals(environment)) {
211
context.produce(new ProductionOptimizationBuildItem());
212
} else if ("development".equals(environment)) {
213
context.produce(new DevelopmentToolsBuildItem());
214
}
215
216
if (debugMode) {
217
context.produce(new DebugInfoBuildItem());
218
}
219
}
220
}
221
222
// Usage in test
223
@RegisterExtension
224
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
225
.withApplicationRoot(jar -> jar.addClasses(MyService.class))
226
.addBuildChainCustomizerEntries(new QuarkusProdModeTest.BuildChainCustomizerEntry(
227
ConditionalBuildStep.class,
228
List.of(ProductionOptimizationBuildItem.class, DevelopmentToolsBuildItem.class, DebugInfoBuildItem.class),
229
List.of()
230
));
231
232
@Test
233
public void testProductionMode() {
234
// Test context will be passed to build step
235
// Build step will produce ProductionOptimizationBuildItem
236
}
237
```
238
239
### Build Step with Resource Processing
240
241
```java
242
public class ResourceProcessingBuildStep extends ProdModeTestBuildStep {
243
244
public ResourceProcessingBuildStep(Map<String, Object> testContext) {
245
super(testContext);
246
}
247
248
@Override
249
public void execute(BuildContext context) {
250
ApplicationArchivesBuildItem archives = context.consume(ApplicationArchivesBuildItem.class);
251
252
for (ApplicationArchive archive : archives.getAllApplicationArchives()) {
253
// Process resources in the archive
254
Path resourcesPath = archive.getArchiveRoot().resolve("META-INF/resources");
255
256
if (Files.exists(resourcesPath)) {
257
try {
258
Files.walk(resourcesPath)
259
.filter(Files::isRegularFile)
260
.forEach(this::processResource);
261
} catch (IOException e) {
262
throw new RuntimeException("Failed to process resources", e);
263
}
264
}
265
}
266
267
// Produce build item indicating resources were processed
268
context.produce(new ProcessedResourcesBuildItem());
269
}
270
271
private void processResource(Path resourcePath) {
272
// Custom resource processing logic
273
Map<String, Object> testContext = getTestContext();
274
String processingMode = (String) testContext.get("resourceProcessingMode");
275
276
// Process based on mode
277
if ("minify".equals(processingMode)) {
278
// Minify resource
279
} else if ("optimize".equals(processingMode)) {
280
// Optimize resource
281
}
282
}
283
}
284
```
285
286
### Testing Build Step Execution
287
288
```java
289
@Test
290
public void testBuildStepExecution() {
291
// Test that custom build steps were executed correctly
292
// This can be verified through:
293
294
// 1. Checking build logs
295
String buildOutput = config.getStartupConsoleOutput();
296
assertTrue(buildOutput.contains("MyCustomBuildStep executed"));
297
298
// 2. Examining build artifacts (if accessible)
299
// 3. Testing runtime behavior that depends on build step output
300
// 4. Using test context to pass verification flags
301
}
302
303
@Test
304
public void testBuildStepWithContext() {
305
// Test context is automatically passed to build steps
306
// You can verify build step behavior by examining the results
307
308
// The build step can access test context and modify behavior accordingly
309
// This allows for parameterized build testing
310
}
311
```
312
313
## Build Item Examples
314
315
```java
316
// Simple build item
317
public class MyBuildItem extends BuildItem {
318
private final String data;
319
320
public MyBuildItem(String data) {
321
this.data = data;
322
}
323
324
public String getData() {
325
return data;
326
}
327
}
328
329
// Complex build item with multiple properties
330
public class ConfigurationBuildItem extends BuildItem {
331
private final Map<String, String> properties;
332
private final List<String> features;
333
334
public ConfigurationBuildItem(Map<String, String> properties, List<String> features) {
335
this.properties = Collections.unmodifiableMap(properties);
336
this.features = Collections.unmodifiableList(features);
337
}
338
339
public Map<String, String> getProperties() {
340
return properties;
341
}
342
343
public List<String> getFeatures() {
344
return features;
345
}
346
}
347
```
348
349
## Best Practices
350
351
### Build Step Design
352
353
1. **Keep build steps focused**: Each build step should have a single responsibility
354
2. **Use test context effectively**: Pass configuration and test data through test context
355
3. **Handle dependencies correctly**: Declare all consumed and produced build items
356
4. **Error handling**: Provide clear error messages for build failures
357
358
### Testing Build Chains
359
360
1. **Verify build item production**: Ensure build steps produce expected build items
361
2. **Test build item consumption**: Verify that build steps properly consume dependencies
362
3. **Integration testing**: Test complete build chains with multiple steps
363
4. **Performance testing**: Measure build step execution time for complex operations