Search code examples
unit-testingjerseytestngcdiweld

How can I enable CDI with Jersey Test Framework?


I found How can I inject a data source dependency into a RESTful web service with Jersey (Test Framework)? but I think I'm gonna ask a little bit different question.

This is a follow-up question of @PostConstruct of abstract ancestors are not invoked

I wrote a JAX-RS library and I'm trying to unit-test with Jersey Test Framework.

I seems HK2 injects properly. But I found some of my life cycle interceptor method annotated with @PostConstruct or @PreDestroy aren't invoked (or only some invoked).

public class MyResource {

    @PostConstruct
    private void constructed() { // not invoked
    }

    @Inject
    private Some some; // injection works.
}

How can I enable CDI with Jersey Test Framework? What kind of artifacts do I have to depend on?

Here is my current dependencies.

<dependency>
  <groupId>javax.inject</groupId>
  <artifactId>javax.inject</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.ws.rs</groupId>
  <artifactId>javax.ws.rs-api</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.test-framework.providers</groupId>
  <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
  <scope>test</scope>
</dependency>

Solution

  • I found a solution.

    I added following additional dependencies.

    <dependency>
      <groupId>org.glassfish.jersey.ext.cdi</groupId>
      <artifactId>jersey-cdi1x</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.ext.cdi</groupId>
      <artifactId>jersey-weld2-se</artifactId>
      <scope>test</scope>
    </dependency>
    

    Now Weld takes over HK2, I think. I don't know what jersey-cdi1x-ban-custom-hk2-binding is for. Anyway, I can use standard annotations from javax.enterprise:cdi-api.

    public class MyProducer {
    
        @Produces @Some
        public MyType produceSome() {}
    
        public void disposeSome(@Disposes @Some MyType instance) {}
    }
    

    And an initialisation code for Weld added.

    @Override
    protected Application configure() {
    
        // this method works somewhat weirdly.
        // local variables including logger
        // is null in here
        // I have to start (and join) a thread
        // which initializes Weld and adds a shutdown hook
    
        final Thread thread = new Thread(() -> {
            final Weld weld = new Weld();
            weld.initialize();
            Runtime.getRuntime().addShutdownHook(
                new Thread(() -> weld.shutdown()));
        });
        thread.start();
        try {
            thread.join();
        } catch (final InterruptedException ie) {
            throw new RuntimeException(ie);
        }
    
        final ResourceConfig resourceConfig
            = new ResourceConfig(MyResource.class);
    
        resourceConfig.register(MyProducer.class);
    
        return resourceConfig;
    }
    

    Every points get injected and all lifecycle methods are invoked. Yay!!!


    I don't understand why I tried to use a thread in the first place.

    @Override
    protected Application configure() {
    
        final Weld weld = new Weld();
        weld.initialize();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> weld.shutdown()));
    
        final ResourceConfig resourceConfig
            = new ResourceConfig(MyResource.class);
    
        resourceConfig.register(MyProducer.class);
    
        return resourceConfig;
    }
    

    Since I use JerseyTestNg.ContainerPerClassTest I failed, at least with TestNG, to work with @BeforeClass and @AfterClass because configure() method is invoked (indirectly) from the constructor.

    I think I can use @BeforeMethod and @AfterMethod for initializing/shutting-down Weld if I switch to JerseyTestNg.ContainerPerMethodTest.


    jersey-cdi1x is a transitive dependency of the jersey-weld2-se so it can be omitted.