OpenWebBeans provides a JUnit 5 integration. It brings an extension which will be backed by CDI Standalone Edition API (SeContainer
).
The entry point is @Cdi
API which maps the main methods of SeContainerInitializer
.
It typically enables you to define which classes you want to deploy for the test or if you want to use classpath scanning.
Here is how a test can look like:
@Cdi(disableDiscovery = true, classes = MyService.class)
class CdiTest {
@Inject
private MyService service;
@Test
void test() {
assertEquals("ok", service.ok());
}
}
As you may notice, this test disable the default classpath scanning and only activate MyService
bean.
Once a test is marked @Cdi
you get injections of the underlying container available in the test class. This is how previous snippet can inject MyService
into the test class without having anything else to do.
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-junit5</artifactId>
<version>${owb.version}</version>
</dependency>
IMPORTANT: this feature comes with OpenWebBeans >= 2.0.11.
@Cdi
also adds a specific API called reusable
. When this is set to true
, the container is started and then reused by next test.
This enables to speed up the test suite a lot when reusing the same JVM to run all tests.
For example if you have these two tests:
@Cdi(reusable = true)
class MyFirstTest {
@Inject
private MyService service;
@Test
void test() {
assertEquals("ok", service.ok());
}
}
and
@Cdi(reusable = true)
class MySecondTest {
@Inject
private MyOtherService service;
@Test
void test() {
assertEquals("ok2", service.ok());
}
}
Then CDI container will be started only once.
However if the first test is:
@Cdi(reusable = true, disableDiscovery = true, classes = MyService.class)
class MyFirstTest {
// .. as before
}
Then the second test will not have its MyOtherService
bean since previous test will only deploy MyService
.
To avoid that it is recommended to follow these best practises:
reusable = true
in your whole code (per execution).To define a reusable setup you just define a new annotation decorated with @Cdi
:
@Target(TYPE)
@Retention(RUNTIME)
@Cdi(reusable = true, disableDiscovery = true, packages = MyService.class)
public @interface TestConfig {
}
Then your test class can look like:
@TestConfig
class MySecondTest {
@Inject
private MyService service;
@Test
void test() {
assertEquals("ok", service.ok());
}
}
For reusable mode to be efficient, it is recommended to use this surefire configuration:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<forkCount>1</forkCount>
</configuration>
</plugin>
And here is how to define executions to mix reusable and not reusable tests - which can have a different deployment setup:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<executions>
<execution> <!-- skip default setup since we redefine it -->
<id>default-test</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
<execution>
<id>not-reusable</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>**/perclass/*</includes>
</configuration>
</execution>
<execution>
<id>reusable</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>**/reusable/*</includes>
</configuration>
</execution>
</executions>
<configuration>
<forkCount>1</forkCount>
</configuration>
</plugin>
This setup assumes not reusable tests are in a perclass
package and reusable ones are in a reusable
package.
Alternatively you can use categories (just define markers in your test code) but this is generally less obvious to manage on the long run.