Why Modularize?
After porting the addon to Quarkus/ArC,
we had two working implementations — but they duplicated the
@TestBean and @TestBeans annotations plus 38
test use-case classes. The Quarkus module also lacked the build quality
plugins (RAT license checks, Checkstyle, Enforcer) that the CDI module
enforced. Time to fix both.
The Target Structure
dynamic-cdi-test-bean-addon/ (parent POM, packaging=pom)
checkstyle.xml (shared at root)
api/ (shared annotations + SPI + usecase test-jar)
cdi-module/ (CDI SE implementation)
quarkus-module/ (ArC implementation)
api
@EnableTestBeans, @TestBean,
@TestBeans, the TestBeanContainerManager
SPI, and a test-jar with 38 use-case classes reused by both
implementations.
cdi
CDI SE implementation using the standard portable extension SPI.
Registers as a TestBeanContainerManager via
ServiceLoader. Profiles for Weld and OpenWebBeans.
quarkus
Quarkus/ArC implementation using BeanProcessor +
BeanRegistrar SPIs. Registers as a
TestBeanContainerManager via
ServiceLoader.
The Key Design Decision
@EnableTestBeans lives in the API module
alongside @TestBean and @TestBeans. A thin
DelegatingJUnitExtension discovers the actual container
manager via ServiceLoader. Each implementation module
registers a TestBeanContainerManager provider.
Users always import the same annotation —
org.os890.cdi.addon.dynamictestbean.EnableTestBeans —
regardless of whether they use CDI SE or Quarkus. Switching runtimes
requires only a Maven dependency change. Zero import changes.
Shared Test Use-Cases via test-jar
Both modules need the same 38 test classes (beans like
GreetingConsumer, interfaces like Greeting,
qualifiers like @Premium). Instead of duplicating them or
creating a fourth module, we use Maven's test-jar goal:
<!-- api/pom.xml -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals><goal>test-jar</goal></goals>
</execution>
</executions>
</plugin>
<!-- cdi-module/pom.xml and quarkus-module/pom.xml -->
<dependency>
<groupId>org.os890.cdi.addon</groupId>
<artifactId>dynamic-cdi-test-bean-addon-api</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
Build Quality: Enforced Everywhere
The parent POM defines all quality plugins in
<pluginManagement> and activates them globally. Every
module gets:
- Apache RAT — license header validation on every source file
- Checkstyle — code style enforcement (no star imports, braces, whitespace)
- Enforcer — Java 25+, Maven 3.6.3+, no javax.* deps, dependency convergence
- Compiler — all warnings treated as errors (
failOnWarning)
dependencyConvergence while keeping all other rules.
What Changed in the Quarkus Module
The Quarkus module's class discovery had to learn a new trick. When
use-case classes moved from target/test-classes/ (filesystem)
to the API test-jar (classpath JAR), the directory scanner couldn't find
them. The fix: scan both the filesystem and classpath JARs
containing org/os890/cdi/addon/dynamictestbean/usecase/.
// Scan classpath JARs (e.g., API test-jar with usecase classes)
Enumeration<URL> resources = cl.getResources(
"org/os890/cdi/addon/dynamictestbean/usecase/");
while (resources.hasMoreElements()) {
URL resUrl = resources.nextElement();
if ("jar".equals(resUrl.getProtocol())) {
scanJar(resUrl, cl, classes);
}
}
The Result
| Module | Tests | RAT | Checkstyle | Enforcer |
|---|---|---|---|---|
| api | 0 (test-jar) | 0 unapproved | 0 violations | pass |
| cdi | 64 | 0 unapproved | 0 violations | pass |
| quarkus | 64 | 0 unapproved | 0 violations | pass |
mvn clean verify at the project root.
For Consumers
Pick the implementation that matches your runtime:
<!-- CDI SE (Weld / OpenWebBeans) -->
<dependency>
<groupId>org.os890.cdi.addon</groupId>
<artifactId>dynamic-cdi-test-bean-addon-cdi</artifactId>
<scope>test</scope>
</dependency>
<!-- Quarkus / ArC -->
<dependency>
<groupId>org.os890.cdi.addon</groupId>
<artifactId>dynamic-cdi-test-bean-addon-quarkus</artifactId>
<scope>test</scope>
</dependency>
The API module (@EnableTestBeans, @TestBean,
@TestBeans) is pulled in transitively. Same annotations,
same imports, same test patterns. Switching runtimes is a Maven
dependency change — zero import changes in test classes.