Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Weld + JUnit = Easy Testing of CDI Beans

DZone's Guide to

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.

Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

There 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, @PersistenceContextand @PersistenceUnitinjection 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.
 

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
cdi ,weld ,junit 5 ,performance ,CDI bean ,unit testing

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}