Enterprise Java

Integration testing on REST urls with Spring Boot

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> T getEntity(final String requestMappingUrl, final Class<T> 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<String> requestEntity = new HttpEntity<String>(new HttpHeaders());
		try {
			// Do a call the the url
			final ResponseEntity<T> 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<serviceListReturnTypeClass>
	 * @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> List<T> getList(final String requestMappingUrl, final Class<T> serviceListReturnTypeClass, final Object... parametersInOrderOfAppearance) {
		final ObjectMapper mapper = new ObjectMapper();
		final TestRestTemplate restTemplate = new TestRestTemplate();
		final HttpEntity<String> requestEntity = new HttpEntity<String>(new HttpHeaders());
		try {
			// Retrieve list
			final ResponseEntity<List> entity = restTemplate.exchange(getBaseUrl() + requestMappingUrl, HttpMethod.GET, requestEntity, List.class, parametersInOrderOfAppearance);
			final List<Map<String, String>> entries = entity.getBody();
			final List<T> returnList = new ArrayList<T>();
			for (final Map<String, String> 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> T postEntity(final String requestMappingUrl, final Class<T> serviceReturnTypeClass, final Object objectToPost) {
		final TestRestTemplate restTemplate = new TestRestTemplate();
		final ObjectMapper mapper = new ObjectMapper();
		try {
			final HttpEntity<String> requestEntity = new HttpEntity<String>(mapper.writeValueAsString(objectToPost));
			final ResponseEntity<T> entity = restTemplate.postForEntity(getBaseUrl() + requestMappingUrl, requestEntity, serviceReturnTypeClass);
			return entity.getBody();
		} catch (final Exception ex) {
			// Handle exceptions
		}
		return null;
	}
}
Reference: Integration testing on REST urls with Spring Boot from our JCG partner Ties van de Ven at the JDriven blog.

Arthur Arts

Arthur Arts is an experienced Enterprise Java software engineer and hands-on Agile Coach and Scrum Master. He has a strong focus on agile engineering principles and practices and loves to share his knowledge and learn from others. He has his own company Coded Value, is a passionate photographer and writes for agilearts.nl.
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