REST

Integration testing on REST urls with Spring Boot

Posted on by  
Ties van de Ven

We are building a Spring Boot application with a REST interface and at some point we wanted to test our REST interface, and if possible, integrate this testing with our regular unit tests. One way of doing this, would be to @Autowire our REST controllers and call our endpoints using that. However, this won't give full converage, since it will skip things like JSON deserialisation and global exception handling. So the ideal situation for us would be to start our application when the unit test start, and close it again, after the last unit test. It just so happens that Spring Boot does this all for us with one annotation: @IntegrationTest. Here is an example implementation of an abstract class you can use for your unit-tests which will automatically start the application prior to starting your unit tests, caching it, and close it again at the end.

package demo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import com.fasterxml.jackson.databind.ObjectMapper;

@RunWith(SpringJUnit4ClassRunner.class)
// Your spring configuration class containing the @EnableAutoConfiguration
// annotation
@SpringApplicationConfiguration(classes = Application.class)
// Makes sure the application starts at a random free port, caches it throughout
// all unit tests, and closes it again at the end.
@IntegrationTest("server.port:0")
@WebAppConfiguration
public abstract class AbstractIntegrationTest {

    // Will contain the random free port number
    @Value("${local.server.port}")
    private int port;

    /**
     * Returns the base url for your rest interface
     *
     * @return
     */
    private String getBaseUrl() {
        return "http://localhost:" + port;
    }

    // Some convenience methods to help you interact with your rest interface

    /**
     * @param requestMappingUrl
     *            should be exactly the same as defined in your RequestMapping
     *            value attribute (including the parameters in {})
     *            RequestMapping(value = yourRestUrl)
     * @param serviceReturnTypeClass
     *            should be the the return type of the service
     * @param parametersInOrderOfAppearance
     *            should be the parameters of the requestMappingUrl ({}) in
     *            order of appearance
     * @return the result of the service, or null on error
     */
    protected T getEntity(final String requestMappingUrl, final Class serviceReturnTypeClass, final Object... parametersInOrderOfAppearance) {
        // Make a rest template do do the service call
        final TestRestTemplate restTemplate = new TestRestTemplate();
        // Add correct headers, none for this example
        final HttpEntity requestEntity = new HttpEntity(new HttpHeaders());
        try {
            // Do a call the the url
            final ResponseEntity entity = restTemplate.exchange(getBaseUrl() + requestMappingUrl, HttpMethod.GET, requestEntity, serviceReturnTypeClass,
                    parametersInOrderOfAppearance);
            // Return result
            return entity.getBody();
        } catch (final Exception ex) {
            // Handle exceptions
        }
        return null;
    }

    /**
     * @param requestMappingUrl
     *            should be exactly the same as defined in your RequestMapping
     *            value attribute (including the parameters in {})
     *            RequestMapping(value = yourRestUrl)
     * @param serviceListReturnTypeClass
     *            should be the the generic type of the list the service
     *            returns, eg: List * @param parametersInOrderOfAppearance
     *            should be the parameters of the requestMappingUrl ({}) in
     *            order of appearance
     * @return the result of the service, or null on error
     */
    protected List getList(final String requestMappingUrl, final Class serviceListReturnTypeClass, final Object... parametersInOrderOfAppearance) {
        final ObjectMapper mapper = new ObjectMapper();
        final TestRestTemplate restTemplate = new TestRestTemplate();
        final HttpEntity requestEntity = new HttpEntity(new HttpHeaders());
        try {
            // Retrieve list
            final ResponseEntity entity = restTemplate.exchange(getBaseUrl() + requestMappingUrl, HttpMethod.GET, requestEntity, List.class, parametersInOrderOfAppearance);
            final List> entries = entity.getBody();
            final List returnList = new ArrayList();
            for (final Map entry : entries) {
                // Fill return list with converted objects
                returnList.add(mapper.convertValue(entry, serviceListReturnTypeClass));
            }
            return returnList;
        } catch (final Exception ex) {
            // Handle exceptions
        }
        return null;
    }

    /**
     *
     * @param requestMappingUrl
     *            should be exactly the same as defined in your RequestMapping
     *            value attribute (including the parameters in {})
     *            RequestMapping(value = yourRestUrl)
     * @param serviceReturnTypeClass
     *            should be the the return type of the service
     * @param objectToPost
     *            Object that will be posted to the url
     * @return
     */
    protected T postEntity(final String requestMappingUrl, final Class serviceReturnTypeClass, final Object objectToPost) {
        final TestRestTemplate restTemplate = new TestRestTemplate();
        final ObjectMapper mapper = new ObjectMapper();
        try {
            final HttpEntity requestEntity = new HttpEntity(mapper.writeValueAsString(objectToPost));
            final ResponseEntity entity = restTemplate.postForEntity(getBaseUrl() + requestMappingUrl, requestEntity, serviceReturnTypeClass);
            return entity.getBody();
        } catch (final Exception ex) {
            // Handle exceptions
        }
        return null;
    }
}

Continue reading →

shadow-left