Quicker JerseyTest

April 9th, 2012

That is a quick one. These days I was playing around with Jersey in order to create a rest web service. The thing is that I would like to test what I create. The good thing is that Jersey provides a testing framework. But...

Jersey test setup

As the project I was working on was a mavenized one, all I had to do was to add the dependency:

<dependency>
  <groupId>com.sun.jersey.jersey-test-framework</groupId>
  <artifactId>jersey-test-framework-grizzly2</artifactId>
  <version>${jersey.version}</version>
  <scope>test</scope>
</dependency>

As a quick reminder, you can not only use the grizzly web container with the jersey framework, but as I needed some a way to test the actual http api, I went that way initializing everything I would have initialize at a web container with an in memory database.

So after that you can extend the com.sun.jersey.test.framework.JerseyTest class and use it with jUnit.

The problem

The problem is that the JesreyTest initializes the webcontainer before each test and shut it down after that. That means that the procedure became real slow and I could not run parallel tests as the testing framework would try to initialize the web container twice causing an exception regarding the address that is already in use.

The solution

After reading the JerseyTest source code I ended up at the setUp method:

@Before
public void setUp() throws Exception {
  tc.start();
}

but as tc was the com.sun.jersey.test.framework.spi.container.TestContainer class that is getting initialized at the contstractor of the JerseyTest, there was not anything that I could do there!.

As I used the JerseyTest(AppDescriptor ad) you can see here:

public JerseyTest(AppDescriptor ad) throws TestContainerException {
  this.tc = getContainer(ad, getTestContainerFactory());
  this.client = getClient(tc, ad);
}

I could change two things. Either the getContainer method or the getTestContainerFactory. From these two, only the second one was protected and could let me do something like that.

One TestContainer per AppDescriptor

So what I needed was a custom TestContainerFactory that would cache the resulted TestContainer per AppDescriptor. Something like:

class OnePerAppDescriptorTestContainerFactory implements TestContainerFactory {
  private static final ConcurrentMap<AppDescriptor, TestContainer> cache = new ConcurrentHashMap<AppDescriptor, TestContainer>();

  private final TestContainerFactory tcf;

  public OnePerAppDescriptorTestContainerFactory(TestContainerFactory tcf) {
    this.tcf = tcf;
  }

  @Override
  public <T extends AppDescriptor> Class<T> supports() {
    return tcf.supports();
  }

  @Override
  public TestContainer create(URI baseUri, AppDescriptor ad) {
    if (cache.get(ad) == null) {
      cache.putIfAbsent(ad, tcf.create(baseUri, ad));
    }
    return cache.get(ad);
  }
}

Now I can wrap any other TestContainerFactory and only use it if I do not have any other TestContainer ready for a given AppDescriptor. The last problem would be a shutdown hook. As I'm using TestNG the @BeforeSuite with @AfterSuite annotations worked just fine:

public class BetterJerseyTest extends JerseyTest {

  //Fire up jersey with Guice
  private static final AppDescriptor APP_DESCRIPTOR = new WebAppDescriptor.Builder("com.some.package.name")
    .filterClass(GuiceFilter.class)
    .contextPath("jersey-ctx-path")
    .servletPath("/")
    .clientConfig(new DefaultClientConfig(JacksonJaxbJsonProvider.class))
    .build();

  public JerseyTestNG() {
    super(APP_DESCRIPTOR);
  }

  @Override
  protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
    return new OnePerAppDescriptorTestContainerFactory(super.getTestContainerFactory());
  }

  @BeforeSuite
  public void initTestContainer() throws Exception {
    setUp();
  }

  @AfterSuite
  public void tearDownTestContainer() throws Exception {
    tearDown();
  }
}

The jUnit users may have a look here to check how they can implement a shutdown hook or just don't at all :-).

valotas.com v3.13.1 © Georgios Valotasios - CSS inspired by Adam Wathan's blog