04 September 2019

Introduction

Arquillian is a testing-framework for Jakarta EE applications (formerly Java EE).

System-tests are run as a remote-client invoking a boundary-component of the tested application; e.g. an exposed JAX-RS/REST-endpoint.

Integration-tests are run within the container; allowing to test internal components of the application; e.g. you can inject an EJB or CDI-bean into your test and invoke a method.

Both types of tests have advantages and disadvantages where I find that the disadvantages of Integration-tests often outweight the benefits (in my projects).

Note
You can find a good introduction on different testing-techniques and their advantages/disadvantages in this article series by Sebastian Daschner.

Let me explain: The Jakarta EE applications that I am involved with are usually large, business-focused applications. This means, that I am rarely interested in testing the framework or the container. I am interested in testing how the application behaves in the correct way from a business-perspective. This can often be done quiet nice by calling external REST endpoints. My development-cycle involves a deployed/running application that allows me to hot-swap small code-changes (e.g. via Java’s remote-debugging API) and then invoke the system-test again to see if I get the expected result. Rinse and repeat.

Integration-tests on the other-hand don’t allow me the quick feedback cycle I get from system-tests. As the tests themselfs run in the server/application (and thus are deployed as part of the main WAR/EAR), I have to deploy a whole WAR/EAR to the app-server, run the tests and shut down the container again. If i make a change to the application-code or test, I have to repeat this rather long cycle where I do a full deployment.

The cycle is especially long when the application is not very modular/loosely coupled. Arquillian theoretically allwows me to build small test-deployments with Shrinkwrap but depending on the application the test-archive often has same magnitude as the whole application. So, deployment and thus testing is slow.

What I somtimes would like to have is the quick feedback-loop I get with system-tests but beeing able to test internals of the application that are not exposed via a Rest-endpoint.

How can we get integration-tests that behave more like system-tests? How can we get system-tests that allow us to call internal components of the application?

WarpUnit

Meet WarpUnit. I have been reading about it some time ago and found the idea quiet nice. It is a small testing-solution which allows you to run a system-test but be able to have snippets of code (lambda expressions) that are invoked within the container on server-side. Actually, the approach even allows injection of server-components similar to Arquillian’s integration-tests. Have a look at this very neat concept.

public class GreeterGrayBoxTest {

    @Inject
    Greeter greeter;

    @Test
    public void testGreeter() {

        System.out.println("This is printed in the JUnit test output");

        WarpGate gate = WarpUnit.builder()
                .primaryClass(GreeterGrayBoxTest.class)
                .createGate();

        String greetingForBob = gate.warp(() -> {
            System.out.println("This is printed in the server log");
            return greeter.greet("Bob");
        });

        Assert.assertEquals("Greetings, Bob !",greetingForBob);
    }
}

What happens here is that the gate.warp()-call will take the bytecode of our GreeterGrayBoxTest class, upload it to the server, load it via a custom class-loader and invoke the lambda within the server. Even though the repo did not see a commit for a long time, the solution works when you use it with a recent Wildfly or Liberty. (Actually, the maintainers invited me to contribute and I made a small pull-request to fix the build; a jboss/redhat maven repo URL had changed.)

Note
Just found out about Arquillian Warp which seems to follow a similar approach.

Taking it to the next Level

What I would like to have as a final solution is something that can transparently run as an Arquillian integration-test but can also be invoked like a WarpUnit-style test from outside the application-server.

You can find my proof-of-concept solution on GitHub.

@RunWith(Warp.class)
public class ArquillianStyleIntegrationTest {

    @Inject
    Greeter greeter;

    @Test
    public void testGreeter() {
        System.out.println("This is printed in the server log");

        String result =  greeter.greet("Bob");

        assertThat(result, is("Greetings, Bob !"));
    }
}

Here, the whole testGreeter method is run within the application-server instead of just running some code-snippets in the server. This is a great approach while doing development because I can make quick-changes in my test-code and rerun the test. When I am done, the approach allows me to just swtich the annotation from @RunWith(Warp.class) to @RunWith(Arquillian.class) and I am able to run it as a regular arquillian integration-tests. Obviously, it would be nice to have a deeper arquillian integration that does not require me to change the annotation for this. Instead, it should be transparently handled by an arquillian extension. But this is for the future; after seeing if this approach works in real-world projects.