I\'m trying to use the TestRestTemplate in my Spring Boot Application\'s Integration-Test, to make a request to a Spring Data REST Repository.
The response in the br
I did a similar test, but I'm not using spring-boot. Probably is the configuration of your RestTemplate. By the way, have you tried to use the Traverson
implementation rather than RestTemplate
? It's seems more simple to work with HATEOAS. See bellow my test class with both approaches.
package org.wisecoding.api;
import org.junit.Test;
import org.wisecoding.api.domain.User;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.client.Traverson;
import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.hateoas.client.Hop.rel;
public class UserApiTest {
@Test
public void testGetUsersRestTemplate() {
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new Jackson2HalModule());
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(MediaType.parseMediaTypes(MediaTypes.HAL_JSON_VALUE));
converter.setObjectMapper(mapper);
final List<HttpMessageConverter<?>> list = new ArrayList<HttpMessageConverter<?>>();
list.add(converter);
final RestTemplate restTemplate = new RestTemplate(list);
final String authorsUrl = "http://localhost:8080/apiv1/users";
final ResponseEntity<PagedResources<User>> responseEntity = restTemplate.exchange(authorsUrl, HttpMethod.GET, null, new ParameterizedTypeReference<PagedResources<User>>() {});
final PagedResources<User> resources = responseEntity.getBody();
final List<User> users = new ArrayList(resources.getContent());
}
@Test
public void testGetUsersTraverson() throws Exception {
final Traverson traverson = new Traverson(new URI("http://localhost:8080/apiv1"), MediaTypes.HAL_JSON);
final ParameterizedTypeReference<PagedResources<User>> resourceParameterizedTypeReference = new ParameterizedTypeReference<PagedResources<User>>() {};
final PagedResources<User> resources = traverson.follow(rel("users")).toObject(resourceParameterizedTypeReference);
final List<User> users = new ArrayList(resources.getContent());
}
}
And also, the pom.xml
in case your dependencies does not match:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<groupId>org.wisecoding</groupId>
<version>0.1-SNAPSHOT</version>
<artifactId>user-demo-data-rest</artifactId>
<properties>
<spring.version>4.2.6.RELEASE</spring.version>
<slf4j.version>1.7.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.0.4.v20130625</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-json-org</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
<version>2.5.6.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.10.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.2.3.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>central</id>
<url>http://central.maven.org/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
I switched to MockMvc and everything works:
import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("unittest")
public class MyUserRepositoryIntegrationTest {
@Autowired WebApplicationContext context;
@Autowired FilterChainProxy filterChain;
MockMvc mvc;
@Before
public void setupTests() {
this.mvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filterChain).build();
@Test
public void listUsers() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT, MediaTypes.HAL_JSON_VALUE);
headers.add(HttpHeaders.AUTHORIZATION, "Basic " + new String(Base64.encode(("user:user").getBytes())));
mvc.perform(get(USER_URL).headers(headers))
.andExpect(content().contentTypeCompatibleWith(MediaTypes.HAL_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(NUM_USERS)));
}
}
EDIT:
For those interested, an alternative solution based on Wellington Souza's solution:
While jsonpath is really powerful, I haven't found a way to really unmarshall the JSON into an actual Object with MockMvc.
If you look at my posted JSON output, you'll notice, that it's not the default Spring Data Rest HAL+JSON output. I changed the property data.rest.defaultMediaType to "application/json". With that, I couldn't get Traverson to work either. But when I deactivate that, the following works:
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.client.Hop;
import org.springframework.hateoas.client.Traverson;
import org.springframework.http.HttpHeaders;
import org.springframework.security.crypto.codec.Base64;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("unittest")
public class MyUserRepositoryIntegrationTest {
private static HttpHeaders userHeaders;
private static HttpHeaders adminHeaders;
@LocalServerPort
private int port;
@BeforeClass
public static void setupTests() {
MyUserRepositoryIntegrationTest.userHeaders = new HttpHeaders();
MyUserRepositoryIntegrationTest.userHeaders.add(HttpHeaders.ACCEPT, MediaTypes.HAL_JSON_VALUE);
MyUserRepositoryIntegrationTest.userHeaders.add(HttpHeaders.AUTHORIZATION,
"Basic " + new String(Base64.encode(("user:user").getBytes())));
MyUserRepositoryIntegrationTest.adminHeaders = new HttpHeaders();
MyUserRepositoryIntegrationTest.adminHeaders.add(HttpHeaders.ACCEPT, MediaTypes.HAL_JSON_VALUE);
MyUserRepositoryIntegrationTest.adminHeaders.add(HttpHeaders.AUTHORIZATION,
"Basic " + new String(Base64.encode(("admin:admin").getBytes())));
}
@Test
public void listUsersSorted() throws Exception {
final ParameterizedTypeReference<PagedResources<MyUser>> resourceParameterizedTypeReference = //
new ParameterizedTypeReference<PagedResources<MyUser>>() {
};
final PagedResources<MyUser> actual = new Traverson(new URI("http://localhost:" + port + "/apiv1/data"),
MediaTypes.HAL_JSON)//
.follow(Hop.rel("myUsers").withParameter("sort", "username,asc"))//
.withHeaders(userHeaders)//
.toObject(resourceParameterizedTypeReference);
assertThat(actual.getContent()).isNotNull().isNotEmpty();
assertThat(actual.getContent()//
.stream()//
.map(user -> user.getUsername())//
.collect(Collectors.toList())//
).isSorted();
}
}
(Note: Might not contain all imports etc., since I copied this from a larger Test Class.)
The ".withParam" works for templated URLs, i.e., ones that accept query parameters. If you try to follow the raw URL, it will fail, because the link is literally "http://[...]/users{option1,option2,...}" and thus not well-formed.