I have a REST API outside of my control (supplied by a different, distant team) which I need to consume from a Spring Boot application.
Currently I would like to write a test for that the request (not response) resulting from my RestTemplate invocation corresponds exactly to what is expected at the remote end. I have a sample JSON snippet that I would like to replicate from my code - given the same parameters as in the sample I should get an equivalent JSON snippet in the request body which I would then like to analyze to be certain.
My idea so far is to get RestTemplate to use a server under my control which then captures the JSON request. Apparently MockRestServiceServer
is a good choice for this.
Is this the right approach? How do I configure MockRestServiceServer to allow me to do this?
For example, the method getForObject() will perform a GET and return an object. getForEntity() : executes a GET request and returns an object of ResponseEntity class that contains both the status code and the resource as an object. getForObject() : similar to getForEntity() , but returns the resource directly.
postForEntity. Create a new resource by POSTing the given object to the URL, and returns the response as ResponseEntity . The request parameter can be a HttpEntity in order to add additional HTTP headers to the request. The body of the entity, or request itself, can be a MultiValueMap to create a multipart request.
4.2. Compared to postForObject(), postForEntity() returns the response as a ResponseEntity object. Other than that, both methods do the same job. Let's say that we want to make a POST request to our Person API to create a new Person object and return the response as a ResponseEntity.
If you're only interested in verifying the JSON mapping, you can always use Jackson's ObjectMapper
directly and verify if the object structures match by using a library like JSONassert to verify if the serialized string matches your expected result. For example:
@Autowired
private ObjectMapper objectMapper;
private Resource expectedResult = new ClassPathResource("expected.json");
@Test
public void jsonMatches() {
Foo requestBody = new Foo();
String json = objectMapper.writeValueAsString(requestBody);
String expectedJson = Files
.lines(expectedResult.getFile())
.collect(Collectors.joining());
JSONAssert.assertEquals(expectedJson, json, JSONCompareMode.LENIENT);
}
This test purely uses ObjectMapper
to verify the JSON mapping and nothing else, so you could even do this without actually having to bootstrap Spring boot within your test (which could be faster). The downside of this is that if you're using a different framework than Jackson, or if RestTemplate
changes its implementation, that this test could become obsolete.
Alternatively, if you're interesting in verifying that the complete request matches (both URL, request method, request body and so on), you can use MockRestServiceServer
as you mentioned. This can be done by adding the @SpringBootTest
annotation to your test, autowiring RestTemplate
and the service that invokes RestTemplate
for example:
@RunWith(SpringRunner.class)
@SpringBootTest
public class FooServiceTests {
@Autowired
private RestTemplate restTemplate;
@Autowired
private FooService fooService; // Your service
private MockRestServiceServer server;
@Before
public void setUp() {
server = MockRestServiceServer.bindTo(restTemplate).build();
}
}
You can then set up your tests by using:
@Test
public void postUsesRestTemplate() throws IOException, URISyntaxException {
Path resource = Paths.get(getClass().getClassLoader().getResource("expected-foo.json").toURI());
String expectedJson = Files.lines(resource).collect(Collectors.joining());
server.expect(once(), requestTo("http://example.org/api/foo"))
.andExpect(method(HttpMethod.POST))
.andExpect(MockRestRequestMatchers.content().json(expectedJson))
.andRespond(withSuccess());
// Invoke your service here
fooService.post();
server.verify();
}
As per the documentation, you could match requests using json paths on Mock. For example;
RestTemplate restTemplate = new RestTemplate()
MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
server.expect(ExpectedCount.once(), requestTo(path))
.andExpect(method(HttpMethod.POST))
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].someField").value("some value"))
Note: I haven't tested this.
But I have achieved what you are looking for using Wire Mock many times. That's again a much better option than MockRestServiceServer
. Why do I say so?
Have a look at http://wiremock.org/docs/request-matching/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With