I am writing a JAX-RS (Jersey+Maven) application that does some tricky things (eg call native executables embedded in the WAR). I need to run [some of] my unit tests (JUnit4) on the server (Amazon Elastic Beanstalk running Tomcat 7.0.22) to check that everything is ok.
Is there a standard, flexible way of doing this other than RYO (roll your own)? The things I found seem to have more to do with integration testing on the developer machine (ie, Jersey Test Framework). Even RYO is confusing me… how could I call code in the Test Packages from Source Packages?
Basically, I want to create a /test resource that I can call that will return my unit test results from the server in a pretty format. Even better if I could do /test/{category}
I wanted to share what I’ve learned after posting this question and put up my first answer on StackExchange (a site at which I’ve arrived countless times through google in search of solutions to my endless problems)
The unit vs integration vs functional testing continuum
There a lot of correcting and arguing and trolling on this subject, so I’d like to clear it up. It’s all really very simple. Say you have some service. When you call it there is a chain of events that I’ll simplistically illustrate as:
(request received) – (function 1 called) – (function 2 called) – (function 3 called) – (response sent)
Unit testing tests each function (or class or unit) individually in isolation, feeding in an input and checking the output. Integration testing takes several units (such as the function 2-function 3 chain) and also does the ol’ in-and-out. Functional testing runs through the entire chain, from request to response. I’ll leave it to the reader to guess at some advantages and disadvantages of testing at each level of scale. Anyway, ALL OF THESE TESTS CAN BE RUN IN THE SERVER, AND THERE ARE GOOD REASONS TO RUN THEM THERE.
Types of in-container/in-server testing
a) traditional unit testing (with its advantages of focused and isolated tests) made more convenient
b) closer to the production environment since you’re testing the autowiring logic
e) integrates with IDE test runner
f) quick
a) the environment can be rather different from production
b) doesn’t replace the need for functional testing
a) you have self-contained tests and don’t need to install or set up anything
b) the tests are isolated because a fresh server/container is created for each test or test suite.
b) integrates with IDE test runner
a) the environment can be rather different from production (eg, Jetty isn’t Tomcat or Glassfish)
b) starting/stoping the server slows down the tests c) the frameworks suck. Jeeunit is a tiny project that hasn’t even been tested on Windows, Arquillian is big but very new, poorly documented, and I couldn’t get it to work either.
a) you have plain, old tests that don’t need to be aware of or use any sort of framework
a) no isolation between tests (not necessarily a problem, or even a disadvantage, but may have to take precautions)
b) doesn’t integrate with IDE test runner (at least in Netbeans)
a) it’d done during build (and will integrate with Continuous Integration tools and others)
b) no need to install or set up anything (Maven will download, run, etc the server automatically)
a) the environment can be rather different (Maven uses Jetty, and it runs on your machine)
b) can’t re-run in production
a) EXACTLY the correct environment.
b) run tests whenever
a) need to set up a server
There’s one more point to make. Netbeans gives most of the benefits of Maven testing to in-WAR testing. It includes an embedded server, and starts and deploys to it automatically after build. It even open up Firefox… just set it up to point to your /test resource. It’s just like doing it the Maven way, but better.
Anyway, I’ll show you how to do Maven testing and in-WAR testing together in the same Maven project.
Container-in-the-tests using Spring:
Spring is a sprawling container framework. Its dependency injection mechanisms intertwine with Jax-RS to glorious effect, at the cost of a significant learning curve. I won’t explain how Spring or Jax-RS works. I’ll jump right into the instructions and hopefully readers can adapt the ideas to other scenarios.
The way to get a container going in your JUnit 4 tests is to use the Spring test runner, declare the classes you’d like to register in the container, register some Jax-RS-specific helper classes, register your mocks, and finally use your Jax-RS resource as if it were an ordinary class:
@WebAppConfigurationinjects its own ServletContextAwareProcessor. However,MockServletContextAwareProcessoris necessary when the path to the unpacked WAR file has to be set dynamically, since WebAppConfiguration only lets you set the path statically at compile time. Using this class when running the-tests-in-the-server (see below), I inject the real ServletContext. I used Spring’s profiles feature to suppress it via an environment variable (which isn’t very elegant). setServletContext is called simply by the server test runner.Server-in-the-tests using Maven:
Step 1) Create regular JUnit tests in the /src/test folder, but name them IT*.java or *IT.java or *ITCase.java (eg, MyClassIT.java) You can name them differently, but this is what Failsafe expects by default. IT stands for integration test, but the test code can lie anywhere on the testing continuum. Eg, you can instantiate a class and unit test it, or you can fire up HttpClient (or Jersey Client), point it at yourself (note the port below), and functionally test your entrypoints.
BaseTestClassis just a little helper class that prints the name of the test class and test as it executes (useful for tests-in-server, see below):Step 2) Add maven-failsafe-plugin and maven-jetty-plugin to your pom.xml
Step 3) Profit. Really, that’s it! Just run ‘mvn install’ or hit build in the IDE, and the code will build, your regular *Test.java tests will run, the jetty server will start up, the *IT.java tests will run, and you’ll get a nice report.
Packaging your tests in your WAR to run anywhere:
(use together or separately from above instructions)
Step 1) Get your test classes (the src/test/ directory) embedded in the WAR by instructing the maven-war-plugin to include them: (adapted from here)
Note: You can create a separate WAR with integrated tests by creating an additional execution and in its configuration set and (the details I leave to the reader)
Note: Ideally, the above would exclude all regular tests (and only copy *IT.java) However, I couldn’t get includes/excludes to work.
You will also have to include the test libraries by giving the maven-dependency-plugin an additional execution with a goal of copy-dependency that includes the test scope
If maven-dependency-plugin already has other executions (eg, Netbeans inserts one for javaee-endorsed-api), do not delete them.
Step 2) Programmatically run your tests using JUnitCore (JUnit4).
Step 3) Expose your tests via JAX-RS
Put this class along with your other test classes (in src/test) so that it could reference them.
However, if you are subclassing the javax.ws.rs.core.Application class where you’re registering all your resources, you’ll have a problem referencing TestResource (since source code can’t reference test code). To work around this, create a completely empty dummy TestResource class under src/main/…[same package]… This trick works because the dummy TestResource will be overwritten by the real one during packaging.
Step 4) Set up your IDE to launch/deploy your app and open your browser point to "/test" automatically after build.