MockMVC in junit tests - checking result for List<Object> and Map<Enum, Object>

xross picture xross · Oct 23, 2017 · Viewed 15.5k times · Source

I have problems with mockMVC and test written with that. My tests fails. I have two tests methods:

@Test
public void getPersonsForApiConsumerTest() throws Exception {
    mockMvc.perform(get(getUri("/consumers/1/persons")))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$", hasSize(2)))
            .andExpect(jsonPath("$[1].name", is("Ligza")))
            .andExpect(jsonPath("$[2].name", is("Vekrir")));

}

@Test
public void getPersonsForApiConsumerMapTest() throws Exception {
    mockMvc.perform(get(getUri("/consumers/1/persons/map")))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$[1].name", is("Verkir")))
            .andExpect(jsonPath("$[2].name", is("Ligza")));
}

Rest methods with mapping:

 @RequestMapping(value = "/{consumerId}/persons")
    public List<PersonDto> getPersonsForApiConsumer(@PathVariable("consumerId") Integer consumerId) throws IOException {
        return apiConsumerService.getPersonsListForApiConsumer(consumerId);
    }

    @RequestMapping(value = "/{consumerId}/persons/map")
    public Map<ApiConsumerPersonRole, List<Person>> getPersonsForApiConsumerMap(@PathVariable("consumerId") Integer consumerId) throws IOException {
        return apiConsumerService.getPersonsMapForApiConsumer(consumerId);
    }

By Postman I can gain data such:

for list:

[
    {
        "id": 1,
        "firstName": "Ligza",
        "lastName": "Zetasor",
        "role": "REPRESENTATIVE",
        "phone": "123123123",
        "email": "[email protected]"
    },
    {
        "id": 4,
        "firstName": "Vekrir",
        "lastName": "Omegaram",
        "role": "SECURITY_OFFICER",
        "phone": "354654545",
        "email": "[email protected]"
    }
]

for map:

{
    "SECURITY_OFFICER": [
        {
            "id": 4,
            "firstName": "Vekrir",
            "lastName": "Omegaram",
            "telephone": "354654545",
            "email": "[email protected]"
        }
    ],
    "REPRESENTATIVE": [
        {
            "id": 1,
            "firstName": "Ligza",
            "lastName": "Zetasor",
            "telephone": "123123123",
            "email": "[email protected]"
        }
    ],
    "COMPLAINT_OFFICER": [],
    "LOCAL_REPRESENTATIVE": []
}

I dont have any clue how to write it properly. I want to check the data inside this list and map.

Is it possible to stream it into that collection or check it easily in the way it is wriiten already?


Edit: logs:

java.lang.AssertionError: No value at JSON path "$[1].firstname", exception: No results for path: $[1]['firstname']

    at org.springframework.test.util.JsonPathExpectationsHelper.evaluateJsonPath(JsonPathExpectationsHelper.java:245)
    at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:73)
    at org.springframework.test.web.servlet.result.JsonPathResultMatchers$1.match(JsonPathResultMatchers.java:87)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Process finished with exit code -1

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY], Content-Type=[application/json;charset=UTF-8]}
     Content type = application/json;charset=UTF-8
             Body = {"COMPLAINT_OFFICER":[],"REPRESENTATIVE":[{"id":1,"firstName":"Ligza","lastName":"Zetasor","telephone":"123123123","email":"[email protected]"}],"LOCAL_REPRESENTATIVE":[],"SECURITY_OFFICER":[{"id":4,"firstName":"Vekrir","lastName":"Omegaram","telephone":"354654545","email":"[email protected]"}]}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

java.lang.AssertionError: No value at JSON path "$[1].firstname", exception: Filter: [1]['firstname'] can only be applied to arrays. Current context is: {COMPLAINT_OFFICER=[], REPRESENTATIVE=[{"id":1,"firstName":"Ligza","lastName":"Zetasor","telephone":"123123123","email":"[email protected]"}], LOCAL_REPRESENTATIVE=[], SECURITY_OFFICER=[{"id":4,"firstName":"Vekrir","lastName":"Omegaram","telephone":"354654545","email":"[email protected]"}]}

    at org.springframework.test.util.JsonPathExpectationsHelper.evaluateJsonPath(JsonPathExpectationsHelper.java:245)
    at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:73)
    at org.springframework.test.web.servlet.result.JsonPathResultMatchers$1.match(JsonPathResultMatchers.java:87)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Answer

glytching picture glytching · Oct 23, 2017

The jsonPath helpers are very useful for asserting your responses are valid from the perspective of the consumers of your API; these consumers receive a JSON repsonse so it makes sense to perform your assertions on that JSON.

The following assertions will work for the payload from /consumers/1/persons

.andExpect(jsonPath("$[0].firstName", is("Ligza")))
.andExpect(jsonPath("$[1].firstName", is("Vekrir")))

So, the array is 0-based and you access entries in each element by the (case sensitive) name.

However, if you really want to assert against the original types (e.g. PersonDto) then you can read the entire JSON string and use a JSON mapper (such as Jackson or Gson) to deserialize it back into its original class representation. For example:

MvcResult result = mockMvc.perform(get(getUri("/consumers/1/persons")))
        .andExpect(status().isOk())
        .andReturn();

ObjectMapper mapper = new ObjectMapper();

// this uses a TypeReference to inform Jackson about the Lists's generic type
List<PersonDto> actual = mapper.readValue(result.getResponse().getContentAsString(), new TypeReference<List<PersonDto>>() {});

// now just assert equality (or whatever) against the actual PersonDto