How to extract value from JSON response when using Spring MockMVC
You can simply use JsonPath.read
on the result object:
MvcResult result = mockMvc.perform(post("/api/tracker/jobs/work") .contentType(TestUtil.APPLICATION_JSON_UTF8) .content(TestUtil.convertObjectToJsonBytes(workRequest))) .andExpect(status().isCreated()) .andReturn(); String id = JsonPath.read(result.getResponse().getContentAsString(), "$.id")Work work = workService.findWorkById(id);...
I have managed to solve my problem using Spring MockMVC result handler. I created a testing utility to convert the JSON string back to an object and so allowing me to get the ID.
Conversion Utility:
public static <T> Object convertJSONStringToObject(String json, Class<T> objectClass) throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); JavaTimeModule module = new JavaTimeModule(); mapper.registerModule(module); return mapper.readValue(json, objectClass);}
Unit Test:
@Test@Transactionalpublic void createNewWorkWorkWhenCreatedJobItemAndQuantitiesPoolShouldBeCreated() throws Exception { mockMvc.perform(post("/api/tracker/jobs/work") .contentType(TestUtil.APPLICATION_JSON_UTF8) .content(TestUtil.convertObjectToJsonBytes(workRequest))) .andExpect(status().isCreated()) .andDo(mvcResult -> { String json = mvcResult.getResponse().getContentAsString(); workRequestResponse = (WorkRequestResponse) TestUtil.convertJSONStringToObject(json, WorkRequestResponse.class); }); Work work = workService.findWorkById(workRequestResponse.getWorkId()); assertThat(work.getJobItem().getJobItemName()).isEqualTo(workRequest.getJobItem().getJobItemName()); assertThat(work.getJobItem().getQuantities()).hasSize(workRequest.getQuantities().size()); assertThat(work.getJobItem().getQuantityPools()).hasSize(workRequest.getQuantities().size());}
One way to retrieve an arbitrary, generic value from the JSON response is to leverage the jsonPath() matcher from the MockMVC library and couple it with a custom matcher that captures all values it is asked to match.
First, the custom matcher:
import org.hamcrest.BaseMatcher;/** * Matcher which always returns true, and at the same time, captures the * actual values passed in for matching. These can later be retrieved with a * call to {@link #getLastMatched()} or {@link #getAllMatched()}. */public static class CapturingMatcher extends BaseMatcher<Object> { private List<Object> matchedList = new ArrayList<>(); @Override public boolean matches(Object matched) { matchedList.add(matched); return true; } @Override public void describeTo(Description description) { description.appendText("any object"); } /** * The last value matched. */ public Object getLastMatched() { return matchedList.get(matchedList.size() - 1); } /** * All the values matched, in the order they were requested for * matching. */ public List<Object> getAllMatched() { return Collections.unmodifiableList(matchedList); }}
Now, use the custom matcher to capture values and use the jsonPath() matcher to identify what should be captured:
@Test@WithMockUser(username = "reviewer", authorities = {ROLE_USER})public void testGetRemediationAction() throws Exception { CapturingMatcher capturingMatcher = new CapturingMatcher(); // First request a list of all the available actions mvc.perform(get("/api/remediation/action").accept(VERSION_1_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.content[*].remediations[*].id", hasSize(12))) .andExpect(jsonPath("$.content[*].remediations[*].id", capturingMatcher)); // Grab an ID from one of the available actions Object idArray = capturingMatcher.getLastMatched(); assertThat(idArray).isInstanceOf(JSONArray.class); JSONArray jsonIdArray = (JSONArray) idArray; String randomId = (String) jsonIdArray.get(new Random().nextInt(12)); // Now retrieve the chosen action mvc.perform(get("/api/remediation/action/" + randomId).accept(VERSION_1_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.id", is(randomId)));}