32

I have an endpoint that accepts a POST request. I want to get the ID of the newly created entity from the JSON response.

Below is a segment of my code where I'm attempting to do that.

mockMvc.perform(post("/api/tracker/jobs/work")
        .contentType(TestUtil.APPLICATION_JSON_UTF8)
        .content(TestUtil.convertObjectToJsonBytes(workRequest)))
        .andExpect(status().isCreated());

If I get that ID I'll query the database for the newly created entity and do some assertions like below:

Work work = work service.findWorkById(id);

assertThat(work.getJobItem().getJobItemName()).isEqualTo(workRequest.getJobItem().getJobItemName());
assertThat(work.getJobItem().getQuantities()).hasSize(workRequest.getQuantities().size());
assertThat(work.getJobItem().getQuantityPools()).hasSize(workRequest.getQuantities().size());

3 Answers 3

43

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);

...
Sign up to request clarification or add additional context in comments.

3 Comments

This works just perfectly in my case. Thank you for mentioning JsonPath.
What's JsonPath? In what library can we find it?
import com.jayway.jsonpath.JsonPath
7

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
@Transactional
public 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());
}

1 Comment

Is there another way instead using JavaTimeModule class?
4

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)));
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.