Enterprise Java

Testing REST with multiple MIME types

1. Overview

This article will focus on testing a RESTful Service with multiple Media Types/representations. This is the tenth of a series of articles about setting up a secure RESTful Web Service using Spring and Spring Security with Java based configuration.
 
 
 
 
 
 
The REST with Spring series:

2. Goals

Any RESTful service needs to expose it’s Resources as representations using some sort of Media Type, and in many cases more than a single one. The client will set the Accept header to choose the type of representation it asks for from the service. Since the Resource can have multiple representations, the server will have to implement a mechanism responsible with choosing the right representation – also known as Content Negotiation. Thus, if the client asks for application/xml, then it should get an XML representation of the Resource, and if it asks for application/json, then it should get JSON.

This article will explain how to write integration tests capable of switching between the multiple types of representations that the RESTful Service supports. The goal is to be able to run the exact same test consuming the exact same URIs of the service, just asking for a different Media Type.

3. Testing Infrastructure

We’ll begin by defining a simple interface for a marshaller – this will be the main abstraction that will allow the test to switch between different Media Types:

public interface IMarshaller {
    ...
    String getMime();
}

Then we need a way to initialize the right marshaller based on some form of external configuration. For this mechanism, we will use a Spring FactoryBean to initialize the marshaller and a simple property to determine which marshaller to use:

@Component
@Profile('test')
public class TestMarshallerFactory implements FactoryBean<IMarshaller> {
    @Autowired
    private Environment env;
    public IMarshaller getObject() {
        String testMime = env.getProperty('test.mime');
        if (testMime != null) {
            switch (testMime) {
            case 'json':
                return new JacksonMarshaller();
            case 'xml':
                return new XStreamMarshaller();
            default:
                throw new IllegalStateException();
            }
        }
        return new JacksonMarshaller();
    }
    public Class<IMarshaller> getObjectType() {
        return IMarshaller.class;
    }
    public boolean isSingleton() {
        return true;
    }
}

Let’s look over this:

  • first, the new Environment abstraction introduced in Spring 3.1 is used here – for more on this check out the Properties with Spring article
  • the test.mime property is retrieved from the environment and used to determine which marshaller to create – some Java 7 switch on String syntax at work here
  • next, the default marshaller, in case the property is not defined at all, is going to be the Jackson marshaller for JSON support
  • finally – this BeanFactory is only active in a test scenario, as the new @Profile support, also introduced in Spring 3.1 is used

That’s it – the mechanism is able to switch between marshallers based on whatever the value of the test.mime property is.

4. The JSON and XML Marshallers

Moving on, we’ll need the actual marhsaller implementation – one for each supported Media Type.

For JSON we’ll use Jackson as the underlying library:

public class JacksonMarshaller implements IMarshaller {
    private ObjectMapper objectMapper;
    public JacksonMarshaller() {
        super();
        objectMapper = new ObjectMapper();
    }
    ...
    @Override
    public String getMime() {
        return MediaType.APPLICATION_JSON.toString();
    }
}

For the XML support, the marshaller uses XStream:

public class XStreamMarshaller implements IMarshaller {
    private XStream xstream;
    public XStreamMarshaller() {
        super();
        xstream = new XStream();
    }
    ...
    public String getMime() {
        return MediaType.APPLICATION_XML.toString();
    }
}

Note that these marshallers are not define as Spring components themselved. The reason for that is they will be bootstrapped into the Spring context by the TestMarshallerFactory, so there is no need to make them components directly.

5. Consuming the Service with both JSON and XML

At this point we should be able to run a full integration test against the deployed RESTful service. Using the marshaller is straighforward – an IMarshaller is simply injected directly into the test:

@ActiveProfiles({ 'test' })
public abstract class SomeRestLiveTest {
    @Autowired
    private IMarshaller marshaller;
    // tests
    ...
}

The exact marshaller that will be injected by Spring will of course be decided by the value of test.mime property; this could be picked up from a properties file or simply set on the test environment manually. If however a value is not provided for this property, the TestMarshallerFactory will simply fall back on the default marshaller – the JSON marshaller.

6. Maven and Jenkins

If Maven is set up to run integration tests against an already deployed RESTful Service, then it can be run like this:

mvn test -Dtest.mime=xml

Or, if this the build uses the integration-test phase of the Maven lifecycle:

mvn integration-test -Dtest.mime=xml

For more details about how to use these phases and how to set up the a Maven build so that it will bind the deployment of the application to the pre-integration-test goal, run the integration tests in the integration-test goal and then shut down the deployed service in on post-integration-test, see the Integration Testing with Maven article.

With Jenkins, the job must be configured with:

This build is parameterized

And the String parameter: test.mime=xml added.

A common Jenkins configuration would be having to jobs running the same set of integration tests against the deployed service – one with XML and the other with JSON representations.

7. Conclusion

This article showed how to properly test a REST API. Most APIs do publish their resources under multiple representations, so testing all of these representations is vital, and using the exact same tests for that is just cool.

For a full implementation of this mecahnism in actual integration tests verifying both the XML and JSON representations of all Resources, check out the github project.
 

Reference: Testing REST with multiple MIME types from our JCG partner Eugen Paraschiv at the baeldung blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button