Weld + JUnit = Easy Testing of CDI Beans
Lots can go wrong in a unit test incorrectly performed. Weld now has an extension that could help you keep everything together.
Join the DZone community and get the full member experience.
Join For FreeThere is no doubt that unit testing is essential for many applications. But if you have a unit test for a CDI bean without running actual CDI container, quite a few issues arise. Starting with field injection simulations, then extending to interceptors, decorators, events, programmatic lookup — all in all, it quickly becomes quite a challenge. This is where we usually reach for mocking frameworks. But use too many mocks and things get tangled really quickly. Of course, there are still integration tests. But integration tests are usually more time and resource-consuming.
So here comes Weld JUnit extension, which allows you to use an actual CDI container instead of complex simulations. This extension boots up Weld before each test run and shuts it down afterwards. This means that you can leverage all bean capabilities (injection, interception, events, etc.) in your tests. Of course, you have the power to customize what beans, extensions, interceptors (and so forth) are going to be in the container. Besides, it's easy to combine this approach with mocking frameworks. As a matter of fact, some convenient tools to allow easy mocking are already baked into the extension.
Key Features
Supports JUnit 4 and JUnit 5
Supports Weld 2.4 (CDI 1.2) and Weld 3.0 (CDI 2.0)
Test class injection
Mocking
Convenient tools to add mock beans and interceptors
Mock injection services (e.g. to mock
@Resource
and@EJB
injection points)Mock contexts for normal scopes
Get Started
First, add the Maven artifact to your pom.xml:
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-junit5</artifactId>
<version>${version.weld-junit}</version>
<scope>test</scope>
</dependency>
NOTE: We will be using JUnit 5 for demonstration purposes. If you wish to know more about JUnit 4, please refer to these guidelines.
Let's start with the simplest possible test:
/**
* 1. Weld container is started/stopped automatically.
* 2. By default, only the content of the test package is discovered by Weld.
* 3. Test class is injected automatically.
*/
@EnableWeld
class MyTest {
@Inject
Foo foo;
@Test
void testFooPing() {
Assertions.assertEquals("pong", foo.ping());
}
}
If you want to customize the configuration you would use WeldInitiator
:
@EnableWeld
class MyTest {
@WeldSetup // This tells weld to consider only Bar, nothing else
public WeldInitiator weld = WeldInitiator.of(Bar.class);
@Test // Note that test method params are injected too
public void test(Bar bar) {...}
}
If your bean is @RequestScoped
then you should activate the request context:
@EnableWeld
class MyTest {
@WeldSetup
public WeldInitiator weld = WeldInitiator.from(Baz.class).activate(RequestScoped.class).build();
@Test
public void test(Baz baz) {
// A mocking request context is activated within the test method execution
}
}
If your bean injects a bean you don't have an implementation, simply mock the bean:
interface Bar {
String ping();
}
// This is the bean under the test
class Qux {
@Inject
Bar bar;
String ping() {
return bar.ping();
}
}
@EnableWeld
class MyTest {
@WeldSetup
public WeldInitiator weld = WeldInitiator.from(Qux.class).addBeans(createBarBean()).build();
static Bean<?> createBarBean() {
return MockBean.builder()
.types(Bar.class)
.scope(ApplicationScoped.class)
.creating(
// Mock object provided by Mockito
Mockito.when(Mockito.mock(Bar.class).ping()).thenReturn("pong").getMock())
.build();
}
@Test
public void test(Qux qux) {
Assertions.assertEquals("pong", qux.ping());
}
}
If your bean declares a @Resource
injection point provide a mock resource, as shown below:
class Baz {
@Resource(lookup = "somejndiname")
String coolResource;
}
@EnableWeld
class MyTest {
@WeldSetup
public WeldInitiator weld = WeldInitiator.from(Baz.class).bindResource("somejndiname", "coolString").build();
@Test
public void test(Baz baz) {
Assertions.assertEquals("coolString", baz.coolResource);
}
}
NOTE: @EJB
, @PersistenceContext
and @PersistenceUnit
injection points are also supported.
Conclusion
We have shown that Weld and JUnit play well together. weld-junit allows you to test your CDI beans quickly and easily. If you wish to see more in-depth guide/examples, it's right inside the project. Last but not least, it's an open source project, so feel free to create issues, share ideas, throw feature requests, and send pull requests.
Opinions expressed by DZone contributors are their own.
Comments