Integration Testing or Unit Testing? REST Services and JMock

December 10, 2009

In my last couple of postings I was considering various ways of Testing RESTful services, and specifically described using JUnit to test the service, exploiting the Apache Wink Client libraries. I was interested to see a comment from “Lior” who wanted to run tests as part of an automated build process, and hence wanted to avoid the need to start a server in order to host the service under test. “Ben F” then suggested using a Mock Library to emulate the HTTP Request and Response objects. This reminded me of a few Unit Testing principles and that took me to a whole different way of Unit Testing the service implementation classes. I believe that this approach, which I’ve implemented using JMock, is the key to creating a robust set of true Unit Tests.

So before getting to the explanation of the testing approach, I’d like to look briefly at the meaning of the tests I was previously writing . If, as I now believe they were not “True” Unit Tests what were they? And why does that matter.

Integration Tests and Unit Tests

The essence of the previous testing was that we were testing fully functional code, deployed to WebSphere Application Server (WAS). In fact I had some degree of cheating, in that I was not using a real persistence store but a very simple in-memory collection, but in a real development, I would have been testing my real, functioning code, making database calls etc. I have been doing something much closer to Integration Testing – in that I’m exercising the code I write after is is integrated with much infrastructure code (and my persistence layer.)

There’s no doubt in my mind that such Integraation Tests are valuable, and I am very comfortable in using JUnit for this purpose. Having a battery of such tests is extremely useful for testing in later stages of development, for example we can use them for Soak testing, Performance testing and, by submitting multiple instances simultaneously Stress testing and Concurrency testing too. However, there are several costs to developing Integration tests. First and foremost, as Lior observed, there is a dependency upon a significant stack of software – we can’t run the tests without starting WAS and a Database, and even in the RAD environment this takes some time.  Furthermore the tests tend to be brittle, their correctness depends upon many components. There is much material elsewhere on this topic, for example here (in this reference, the testing we have been doing is termed Functional Testing), so I won’t labour the point and instead cut to the chase:

I want to write Unit tests. That is, tests which explicitly test the code I write to implement my service. While designing these Unit tests I will trust my dependencies such as Apache Wink, WAS and the database and make no attempt to test those dependencies.

We can then see that our Integration Tests serve a significant purpose in verify that our trust in our dependency components is justified. However the ratio of Unit Tests to Integration Tests can be quite heavily biased towards Unit Tests, provided that we can trust our dependencies – and if our dependencies themselves have good Unit tests then we can indeed trust them. [This approach is something of an over-simplification. It is remarkable how many defects are discovered when we first integrate pieces of an application. Hence I am a strong believer in early Integration Testing.]

My challenge then is to write Unit tests for the service code, and the rest of this post will explain how. The beauty of the JAX-RS programming model is that it makes this testing very easy.

Unit Testing POJOs

The key features of programming models such as JAX-RS (and EJB3 and JPA) is that we have simple Plain Old Java Objects (POJOs) that are annotated to enable the application server container runtime to provide infrastructure services. This service implementation code is executable stand-alone, and hence we can test it directly, without using any application server.

    @Path("/editions")
    public class BookEditionResources {

    @Path("{isbn}")
    @GET
    @Produces( MediaType.APPLICATION_JSON)
    public Response getEditionByIsbn(
                     @PathParam("isbn") String isbn ) 
    {
        BookEdition edition
                = EditionStore.getInstance().getEdition(isbn);
        if ( edition != null){   
            return Response.ok(edition).build();
        } else {
            return Response.status(Status.NOT_FOUND).build();
        }
   }

Specifically we can call

        getEditionByIsbn( /* a test value */ )

from our tests. However, things are not quite right for true unit testing, there’s a little work to do:

  • The use of Response objects ties us to the Wink APIs. Although we can use these APIs in Unit tests, the use of Exceptions as discussed in this posting would be make for a cleaner, more easily tested implementation
  • The service classes are currently packaged in a WAR file, and hence are not publically callable by our tests. Some repackaging of the code is needed.
  • There is a dependency on Edition Store, as things stand we’ll not be able to test without whatever persistence mechanism it uses – this leads to the use of Mock objects

I’ll address each of these in turn, first I’ll adjust the implementation delegating the detail of the Response creation to the Wink runtime.

POJO Business Methods

The goal here is to write natural Plain Old Java code, with no special logic relating to it’s role in implementing a RESTful service – all the RESTful characteristic are added by annotation. So first I adjust my service method:

    @Path("{isbn}")
    @GET
    @Produces( MediaType.APPLICATION_JSON)
    public BookEdition getEditionByIsbn (
             @PathParam("isbn") String isbn
             ) throws NotFoundException {
        BookEdition edition
            = getEditionStore().getEdition(isbn);
        if ( edition != null){
            return edition;
        } else {
            throw new NotFoundException("No such isbn:" + isbn);
        }
The important changes here are:

  • The return type is BookEdition not Response, we are delegating Response consturction to Wink.
  • I throw a NotFoundException when the supplied isbn cannot be found.

hence I create a NotFoundException class, extending WebApplicationException and also create a Mapper class

@Provider
public class NotFoundExceptionMapper
   implements ExceptionMapper<NotFoundException>
{
    @Override
    public Response toResponse(NotFoundException ex) {
        System.out.println("Not found Mapping " + ex);
         return Response.status(Status.NOT_FOUND)
                       .entity(ex.toString()).build();
    }
}

Which I register in my WEB-INF/application configuration class.

    org.djna.library.service.BookEditionResources 
    org.djna.library.service.mapper.NotFoundExceptionMapper

This combination of implementation, exception and mapper can be deployed and executed. However it’s not quite ready to test as the packaging needs adjusting, but before we get to that first let’s look at the Unit Tests we’d like to write.

Unit Testing a Service Method

Stripping away all annotations we have this service method

     public BookEdition getEditionByIsbn (
             String isbn ) throws NotFoundException {
        BookEdition edition
            = getEditionStore().getEdition(isbn);
        if ( edition != null){
            return edition;
        } else {
            throw new NotFoundException("No such isbn:" + isbn);
        }

We can see here that the method has the following responsibilities

  • To pass the String isbn parameter to the getEdition() method. Note that this responsibility of the method is very simple: just pass the parameter. [This does make us a little suspicious, should there be some validation of that input? In more realistic implementations inputs might well be validated.]
  • If a non-null the value is returned from getEdition(), return that value.
  • If getEdition() returns null then generate a NotFoundException, which includes a diagnistic indicating which isbn was not found. [This is the stateless service design principle, the client can interpret this response with having retained any record of the original request.]

These responsibilities indicate the major tests that we want to do each of which will require control over the behaviour of getEdition(), which I achieve by using JMock, as I will explain later.:

  1. Verify that the getEdition() method received the expected value. Testing this will require mocking, which I will explain later.
  2. Verify that a non-null value returned from getEdition() is indeed returned. Again, testing this will require a mocking.
  3. Verify the correct production of a NotFoundException.

To give a flavour of the kind of test I am writing, here is a test of the second case, with one crucial detail, the mocking, elided:

        final String isbn = "0863699936";
        final BookEdition expectedEdition
              = new BookEdition("a", "b", isbn, new Date());

        // TODO: Initialise a mock to return that  expectedEdition

        // invoke the service method
        BookEdition retrievedEdition = manager.getEditionByIsbn(isbn);
        assertEquals(edition, retrievedEdition);

The last two lines are the meat of the test, we directly invoke the service method, as a simple Java call, no HTTP involved, and verify that the expected value is obtained.

Next I want to explain now the Unit Test code here is able to compile and execute against the service code, which will eventually be deployed to WAS as a Web Application. To enable that I needed to do a little restructuring of my projects in RAD.

EAR, WAR, JAR and Project Structure

We have three executable pieces to consider: The Service EAR file that will be deployed to WAS, the Client code and the Tests. I need to enable the Client and Test code to use some of the classes contained in the EAR. I achieve this by packaging the service classes in JAR files, which are included in the EAR and also referenced stand-alone by Clients and Tests. Note that the EAR and each JAR file correspond to separate projects in RAD – when we determine the JAR files we are building we also determine the RAD project structure.

This diagram shows the relationships:

Ear

The major features are:

  • The service implementation classes, including the Resource Manager, are moved from the WAR file into their own JAR file, which is included in the EAR as a Utility JAR. Utility JARs in the EAR file are visible to the WAR and hence can still be referenced from the application configuration file.
  • The Wink jar files previously included in the WAR file are now moved to the EAR file also as as Utility JARs. [WAS classloaders are organised such that Utility JARs can “see” each other, but they cannot “see” inside WAR files.]
  • The Entity classes, the JAXB-annotated service payloads are in their own JAR file. This separation allows us to distribute the payload definitions to development teams working on Client code. Such teams (should) not need to see any details of service implementation.
  • Hence, Clients need only the Entity JAR, while Test code needs both Entity and Implementation JARs.

Setting-up these relationships in RAD requires a few adjustments.

Utility JARs in the EAR

The objective is to set up the EAR with Utility JARs as described above.

Ear Structure

My steps were:

  1. Drag the 8 Wink jars (json-20080701.jar etc.) from WEB-INF/lib directory of the Web Application project to the EAR project.
  2. Create an Implementation and Entity projects and move the source code from the Web Application project to those projects. At the moment, the Entity project contains just the payload class, BookEdition.java.
  3. Add the Implementation and Entity projects as Utility Jars in the EAR. Do this in the EAR project’s JEE Module DependenciesJeeModuleDependencies 

WAR file accessing EAR Utility JAR

The Web Application has no classes of its own and hence no compile-time dependencies, however it does need the utility JARs on runtime classpath specified in the Manifest. This is most easily specified via the JEE Module Dependencies of the Web project.

WarModuleDependecies

I add all the Utility Jars.

Unit Test Project – ClassPath

The unit test project needs compile-time and runtime access to the Service implementation and entities and also JUnit and JMock libraries. For simplicity of launching I construct a Build Path that contains everything needed at runtime, even though some of these items are not needed at compile-time.

First the references to the service components.

Project Dependencies

The service components are created from projects in my RAD workspace and so it’s just a matter of adding the Entity and Implementation projects to the Build Path

TestProjectDependencies

Next the libraries containing JUnit and JMock.

Test Libraries

I am using JMock 2.5.1 (download) and this in turn requires JUnit 4.4 (download), which is not the version of JUnit delivered with RAD 7.5. Hence I  downloaded both JMock and JUnit.

I then added the following JARs to the Test project’s library path. From Junit

junit-4.4.jar

And from JMock

hamcrest-core-1.1.jar
hamcrest-library-1.1.jar
jmock-2.5.1.jar
objenesis-1.0.jar
jmock-junit4-2.5.1.jar
cglib-2.1_3-src.jar
jmock-legacy-2.5.1.jar
cglib-nodep-2.1_3.jar

Giving this:

 TestLibraries

Now to use JMock to remove the dependency on the persistence layer.

It’s a Mockery

In order to test without the full persistence layer we need to find a way to “Inject” a mock in its place, and for that we need some cooperation from the service implementation. As things currently stand our service implementation was not designed for testing and so we have no easy way to inject the mock.

So first, a touch of refactoring …

Enable Injection with Dependency Getter

Currently we have this code, using a singleton pattern to get at the persistence layer. [It was quick demo code, all criticisms may be taken as accepted.]

     EditionStore.getInstance().getEdition(isbn);

There are several possible injection techniques we could use ( Martin Fowler  lists a few ) I’m going to use a technique that can be introduced without impacting any existing users of the service.

Rather than directly use the Singleton, I introduce an accessor function:

    protected EditionStore getEditionStore() {
        return EditionStore.getInstance();
    }

The service code is modified to:

    getEditionStore().getEdition(isbn);

You will notice that the Getter is protected, this allows the Unit test to …

Inject a Mock by Overriding the Getter

I create an anonymous class, derived from the class we are testing, the BookEditionResources resource manager and override the getter to inject the mock.

manager = new BookEditionResources(){
            @Override
            protected EditionStore getEditionStore() {
                return mockStore;
            }
        }; 

The derived class implementation methods now all use the mockStore because they use the getter. To complete the picture I need to code to create the mockStore object and code to control and verify the mock’s behaviour.

Setting up the Mock

The full initialisation for the tests looks like this, I’ll explain the details in a moment, but first just not that the bulk of the work is done in the setup()  method, which is annotated with @Before specifying that it is initialisation code run Before each test.

@RunWith(JMock.class)
public class InjectingTest  {

     Mockery mockery;
     EditionStore mockStore;
     BookEditionResources manager;

    @Before
    public void setup() {
        mockery= new JUnit4Mockery() {{
            setImposteriser 
                 (ClassImposteriser.INSTANCE);
        }};

        mockStore = mockery.mock(EditionStore.class);

        manager = new BookEditionResources(){
            @Override
            protected EditionStore getEditionStore() {
                return mockStore;
            }
        };   
    }

The major features here are:

  • @RunWith(JMock.class) annotation instructs JUnit to use a JMock test executor.
  • The Mockery mockery is the factory for the mock objects that the test will use. It is an instance of JUnit4Mockery, that is initialised with a (don’t you love the term?) Imposteriser 
  • The role of the imposteriser ClassImposteriser.INSTANCE is to enable the mocking of concrete classes – by default JMock would only enable the mocking of Interfaces.
  • The mockery then is used to mock the EditionStore, mockStore = mockery.mock(EditionStore.class);
  • that mockStore  is the object I use in the overridden getEditionStore() method.

Setting Expectations

I can now define the tests, whose execution depends upon particular mock object behaviours. To illustrate that, first a test of a successful find

Expection of a Successful Retrieval

    @Test
    public void testRest() throws Exception {
        final String isbn = "0863699936";
        final BookEdition edition
              = new BookEdition("aTitle",
                                "anAuthor",
                                isbn,
                                new Date());

        context.checking(new Expectations() {{
            oneOf (mockStore).getEdition(isbn);
            will(returnValue(edition));
        }});

        BookEdition retrievedEdition
                   = manager.getEditionByIsbn(isbn);
        assertEquals(edition, retrievedEdition);
    }

The test creates an example edition object with a known isbn, and then configures the mock object. Our test is in two parts:

  1. We want to verify that the resource manager’s getEditionByIsbn() method correctly invokes the persistent store with the isbn parameter (yes in this case this is a trivial case, but imagine a more realistic service implementation with serious transformations or complex conditionality)
  2. We want the mock object to return a known, specific value back to getEditionByIsbn(), then later we will test that the expected value was passed back.

hence I specify two mock expectations. In the mockery.checking(new Expectations() {{  …  }}); initialiser I write:

            oneOf (mockStore).getEdition(isbn);

which specifies that the mock should expect exactly one invocation of the getEdition() method, and furthermore I specify the exact expected value of the parameter. Then I also specify what the mock should return with:

            will(returnValue(edition));
       

We now have a fully specified mock for this test case and can code the test:

        BookEdition retrievedEdition
                   = manager.getEditionByIsbn(isbn);
        assertEquals(edition, retrievedEdition);

This is conventional JUnit code, and in the context of our JMock environment should exercise exactly the Unit under test, with no external dependencies. This test includes the assertion checking that the expected value was returned. One final thing remains … did the mock receive exactly the expected calls? That is, were the mockery’s expectations satisfied.

Verifying That the Expectations are Satisfied.

Checking the mockery expectations is something I need to do after every test and so that’s best done in a JUnit @After method:

    @After
    public void verify(){
        mockery.assertIsSatisfied();
    }

Expectations of Not Finding an ISBN

In a similar way I can set up the mock to emulate the case of a lookup of an unknown ISBN

    @Test
    public void testNotFound() {
        final String isbn = "noSuch";

        context.checking(new Expectations() {{
            oneOf (mockStore).getEdition(isbn);
            will(returnValue(null));
        }});

        try {
           @SuppressWarnings("unused")
           BookEdition retrievedEdition
                      = manager.getEditionByIsbn(isbn);
           fail("unexpected success");
        } catch (NotFoundException n){
            assertTrue(n.toString().indexOf(isbn) >=0);
        }   
    }

The mockery is simply configured to return null, which the defined value in the case of an unknown ISBN. The test itself is written to expect an NotFoundException to be thrown, and hence to fail if the lookup should succeed. And the test also checks that there is a meaningful error in the exception – the expectation is that the exception should contain the unfound isbn.

Conclusion

That’s a pretty long explanation of something that in practice takes a very few minutes to set up. If right at the start we plan for testing, create projects in the kind of structure shown here, and design implementation for dependency injection then creating Mocks and Unit Tests is very little effort.

Advertisements

2 Responses to “Integration Testing or Unit Testing? REST Services and JMock”

  1. Lior said

    Thanks for the enriching post, I think you got the point right: if you design your code to be testable it’s a little effort to test it.

  2. Montag said

    Nice one, really enjoyed it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: