I am trying to write a JUnit test, which checks the value of received JSON. I access these values in the JSON using jsonPath. I want to check if a value is true. For simple jsonPaths, it works for me, but when I write more complex jsonPath query, it does not and I am getting this Assertion Error:
Expected: is <true>
but: was <[true]>
My JSON:
{...
,"trips":
[
{...
,"employee":{"name":"Ordinary Joe","login":"joe","contractor":true}
,...
},
...
],
...
}
Problematic assertion
.andExpect(jsonPath("$.trips[?(#.employee.login=='joe')].employee.contractor", is(true)));
What I've tried
I tried to match the value also with new Boolean(true), boolean[] array with one true value and after examination of is() matcher also with String.valueOf(true) however it also did not match.
My question
How should I correctly match this true value? What exactly these [] braces mean in the test output?
In the further investigation, I actually found that jsonPath somehow converts the obtained true value to a JsonArray with one item and therefore it is not possible to match it with is(true) matcher. I am not sure why JsonPath does so.
I found the following working workaround using hasItem matcher instead:
.andExpect(jsonPath("$.trips[?(#.employee.login=='joe')].employee.contractor", hasItem(true)))
.andExpect(jsonPath("$.trips[?(#.employee.login=='joe')].employee.contractor", not(hasItem(false))))
Related
I am trying to print the ids from a JSON response. But I am not able to understand why I am getting a blank list. I have verified the JSONpath (SECTIONS_IDS_JSONPATH) from the online website and it is giving me correct results.
public static void main(String[] args) {
String SECTIONS_IDS_JSONPATH = "$.[*].instructionEvents[*].sectionId";
String sectionsData = "{\"sections\":[{\"id\":\"8da1cf5d-3150-4e11-b2af-338d1df20475\",\"courseId\":\"e8a65581-ed1c-43f0-90a7-7b9d51b35062\",\"courseCredits\":[{\"minimum\":4,\"maximum\":null,\"measure\":\"hour\",\"increment\":null}],\"academicPeriodId\":\"8b7a8e9e-5417-42a3-9c90-8d47226b5987\",\"reservedSeatsMaximum\":0,\"maxEnrollment\":0,\"hours\":[],\"sites\":[\"All Campuses\"],\"instructors\":[],\"instructionEvents\":[{\"id\":\"9d0c49e2-1579-43c3-b25a-2f85f551e62d\",\"sectionId\":\"8da1cf5d-3150-4e11-b2af-338d1df20475\",\"courseId\":\"e8a65581-ed1c-43f0-90a7-7b9d51b35062\",\"days\":[\"monday\",\"wednesday\",\"friday\"],\"startTm\":\"2019-01-01T09:45:00-05:00\",\"endTm\":\"2024-12-01T10:45:00-05:00\",\"localizations\":[],\"instructionalMethod\":\"Lecture\"}]},{\"id\":\"ad3f63ad-e642-4938-a9fd-318afd2d1ad0\",\"courseId\":\"e8a65581-ed1c-43f0-90a7-7b9d51b35062\",\"courseCredits\":[{\"minimum\":4,\"maximum\":null,\"measure\":\"hour\",\"increment\":null}],\"academicPeriodId\":\"8b7a8e9e-5417-42a3-9c90-8d47226b5987\",\"reservedSeatsMaximum\":0,\"maxEnrollment\":20,\"hours\":[],\"sites\":[\"All Campuses\"],\"instructors\":[{\"id\":\"c26572de-f9c8-4623-ba6a-79997b33f1c6\",\"sectionId\":\"ad3f63ad-e642-4938-a9fd-318afd2d1ad0\",\"role\":\"primary\",\"persons\":[{\"id\":\"c1b50d79-5505-4a33-9316-b4b1f52c0ca3\",\"names\":[{\"firstName\":\"BanColoFac-1\",\"lastName\":\"CTester\",\"preferred\":true}]}]}],\"instructionEvents\":[{\"id\":\"af8fb500-29f5-4451-95d5-a11215298cd4\",\"sectionId\":\"ad3f63ad-e642-4938-a9fd-318afd2d1ad0\",\"courseId\":\"e8a65581-ed1c-43f0-90a7-7b9d51b35062\",\"days\":[\"tuesday\",\"thursday\"],\"startTm\":\"2019-01-01T10:00:00-05:00\",\"endTm\":\"2024-12-01T10:50:00-05:00\",\"localizations\":[],\"instructionalMethod\":\"Lecture\"}]},{\"id\":\"a1422391-e2b9-4bc4-907b-371fcea01d70\",\"courseId\":\"e8a65581-ed1c-43f0-90a7-7b9d51b35062\",\"courseCredits\":[{\"minimum\":4,\"maximum\":null,\"measure\":\"hour\",\"increment\":null}],\"academicPeriodId\":\"8b7a8e9e-5417-42a3-9c90-8d47226b5987\",\"reservedSeatsMaximum\":0,\"maxEnrollment\":20,\"hours\":[],\"sites\":[\"All Campuses\"],\"instructors\":[{\"id\":\"808daae1-3ec6-47ec-9af0-5392199bdf78\",\"sectionId\":\"a1422391-e2b9-4bc4-907b-371fcea01d70\",\"role\":\"primary\",\"persons\":[{\"id\":\"793cc9b3-57c7-4a2d-8984-07a1fb6834a9\",\"names\":[{\"firstName\":\"Andrew\",\"lastName\":\"Adams\",\"preferred\":true}]}]}],\"instructionEvents\":[{\"id\":\"730b4206-684d-4413-bf20-9bec5c1dc900\",\"sectionId\":\"a1422391-e2b9-4bc4-907b-371fcea01d70\",\"courseId\":\"e8a65581-ed1c-43f0-90a7-7b9d51b35062\",\"days\":[\"tuesday\",\"thursday\"],\"startTm\":\"2019-01-01T10:00:00-05:00\",\"endTm\":\"2024-12-01T10:50:00-05:00\",\"localizations\":[],\"instructionalMethod\":\"Lecture\"},{\"id\":\"8bc059ab-a8f8-4469-8e79-bbc71f7fa3fd\",\"sectionId\":\"a1422391-e2b9-4bc4-907b-371fcea01d70\",\"courseId\":\"e8a65581-ed1c-43f0-90a7-7b9d51b35062\",\"days\":[\"monday\",\"wednesday\",\"friday\"],\"startTm\":\"2019-05-26T09:00:00-04:00\",\"endTm\":\"2021-05-26T09:50:00-04:00\",\"localizations\":[],\"instructionalMethod\":\"Lecture\"}]}]}";
List<String> ids = JsonPath.parse(sectionsData).read(SECTIONS_IDS_JSONPATH);
System.out.println(ids);
}
Alright, since this question might get delete if nothing else ever happens I better post this as an answer.
As explained by Andreas you should use JSONPath $.*[*].instructionEvents[*].sectionId instead. Quoting fromt the comment
The syntax $.[*] is undefined, I can't find any documentation/example
doing that. The JSONPath Online Evaluator [*based on
JSONPath-Plus implemented in JavaScript] treats it as $..[*], but the Java library treats
it differently. Since the outer part of the JSON is {"sections":[ ... ]}, you have an object, so you need a property selector (.prop or
.*). Once you've selected your property (.sections, or .*
since there's only one), the property is an array, so you need an
array selector ([2] or [*]). Hence you can use $.sections[*] or
$.*[*] to match all sections.
Indeed, looking at this massive JSONPath Comparision we can see that the syntax in question is not listed for any implementation.
I have an API endpoint which, when called with GET, returns an array of JSON objects in the body, like this:
[
{"id": "321", "created": "2019-03-01", "updated": "2019-03-15"},
{"id": "123", "created": "2019-03-02", "updated": "2019-03-16"}
]
I would like to check the body with a Spring MockMvc test case.
The statement currently looks like this:
mockMvc.perform(get("/myapi/v1/goodstuff").
andExpect(status().isOk()).
andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
andExpect(jsonPath("$.*", isA(ArrayList.class))).
andExpect(jsonPath("$.*", hasSize(2))).
andExpect(jsonPath("$[0].id", is("321"))).
andExpect(jsonPath("$[0].created", is("2019-03-01"))).
andExpect(jsonPath("$[0].updated*", is("2019-03-15"))).
andExpect(jsonPath("$[1].id", is("1232"))).
andExpect(jsonPath("$[1].created", is("2019-03-02"))).
andExpect(jsonPath("$[1].updated*", is("2019-03-16")));
However, the implementation of my API doesn't guarantee the order of JSON object in the returned array.
Were this an array of strings, I would solve this via matcher generated by org.hamcrest.collection.IsIterableContainingInAnyOrder<T>.containsInAnyOrder.
But I cannot see any suitable matcher for my situation in their doc, nor any clue in the description of jsonPath method in Spring docs
From a quick search I didn't manage find anything related to my situation on SO, either, beyond a list of strings situation I described above.
Of course, I could convert JSON objects to strings.
But I'm wondering, could I solve this problem for a list of JSON objects, comparing each of the fields of each objects one-by-one (like shown in the code snippet above), but ignoring the order of objects in the collection?
Update: Zgurskyi has suggested a solution that helps with my original simplified example. However, with a real-life practical example there are 2 more inputs:
the number of fields is 10-20 instead of 3
not all of matchers are plain is, for instance:
(a bit closer to my original code)
mockMvc.perform(get("/myapi/v1/greatstuff").
andExpect(status().isOk()).
andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
andExpect(jsonPath("$.*", isA(ArrayList.class))).
andExpect(jsonPath("$.*", hasSize(2))).
andExpect(jsonPath("$[0].id", is("321"))).
andExpect(jsonPath("$[0].did", anything())).
andExpect(jsonPath("$[0].createdTs", startsWith("2019-03-01"))).
andExpect(jsonPath("$[0].updatedTs", startsWith("2019-03-15"))).
andExpect(jsonPath("$[0].name", equalToIgnoringCase("wat"))).
andExpect(jsonPath("$[0].stringValues", containsInAnyOrder("a","b","c"))).
andExpect(jsonPath("$[1].id", is("1232"))).
andExpect(jsonPath("$[1].did", anything())).
andExpect(jsonPath("$[1].createdTs", startsWith("2019-03-01"))).
andExpect(jsonPath("$[1].updatedTs", startsWith("2019-03-15"))).
andExpect(jsonPath("$[1].name", equalToIgnoringCase("taw"))).
andExpect(jsonPath("$[1].stringValues", containsInAnyOrder("d","e","f"))).
andReturn();
So far it seems that I can't do anything better than implementing my own matcher class.
Or...can I?
You can assert list items fields ignoring order:
.andExpect(jsonPath("$[*].id", containsInAnyOrder("321", "123")))
.andExpect(jsonPath("$[*].created", containsInAnyOrder("2019-03-01", "2019-03-02")))
.andExpect(jsonPath("$[*].updated", containsInAnyOrder("2019-03-15", "2019-03-16")))
Another approach would be to check that specific list items exist in response:
.andExpect(jsonPath("$.[?(#.id == 123 && #.created == \"2019-03-02\" && #.updated == \"2019-03-16\")]").exists())
.andExpect(jsonPath("$.[?(#.id == 321 && #.created == \"2019-03-01\" && #.updated == \"2019-03-15\")]").exists())
Additionally there is another way to assert the json without being strict about order using MockMvcResultMatchers
.andExpect(MockMvcResultMatchers.content().json(<json-here>, false))
By setting the strict=false, it can do a fussy search.
I think a better solution could be something like that:
.andExpect(jsonPath("$.violations", hasSize(3)))
.andExpect(jsonPath("$.violations", containsInAnyOrder(
Map.of("field", "name", "message", "must not be empty"),
Map.of("field", "email", "message", "must not be empty"),
Map.of("field", "birthdate", "message", "must not be null")
)
))
It worked for me, but I have to be honest, I don't like to use Map instead a domain type, like Violation, Tuple, Category etc. Unfortunately, I could not make it work with a type different than Map.
I have json for example below
{"TestJson":{
"Result":"Passed",
"description":"Passed."},
"Students":[{
"Class":{
"Primary":"Yes"
},
"Course":{
"Enrolled":"yes",
"AccountNumber":"2387287382"
},
"AccountNumber":"2387287382",
"Paid":"Yes"
}]}
I am wondering how can I find a good solution for this.
What I currently do
.body("Students[0].Course.AccountNumber",equalTo("2387287382"))
.body("Students[0].AccountNumber",equalTo("2387287382"))
My test criteria is to check key Students[0].AccountNumber matches Students[0].Course.AccountNumber
I want to do in this way, but i am not able to find a solution something like
.body("Students[0].Course.AccountNumber",equalTo("Students[0].AccountNumber"))
The above wont work obviously, but that is how I want to compare. basically comparing the key with another key and they should match.
Is this doable?
One way to do it is:
String A =
given ().
when().
get/put/post({api/name})
.extract()
.path("Students[0].Course.AccountNumber");
String B =
given ().
when().
get/put/post({api/name})
.extract()
.path("Students[0].AccountNumber");
Assert.assertEquals(A, B);
Seems like this workaround is the only way to go.
See the Use the response to verify other parts of the response section of the rest-assured docs. You basically want to create a lambda implementing ResponseAwareMatcher<Response>. Something like this:
get("/x").then().body("href", response -> equalTo("http://localhost:8080/" + response.path("userId"));
I am getting the following error using Hamcrest Matcher library.
"The method assertThat(T, Matcher) in the type Assert is not applicable for the arguments (List, AnyOf)"
What I am trying is
Sample code
List<String> poster_path_image2;
assertThat(poster_path_image2, anyOf(startsWith("https:"), startsWith("null")));
I need to check if a url is valid and null value is acceptable as well.
I am new to this library and stumped by this error.
It looks like poster_path_image2 is of type List. but the Matcher startsWith can just work on String. Check the types of your variables and what the matcher is able to process.
Maybe you want to get the first element of your List or repeat the assertion for every item in the list.
String path = "your test String";
assertThat(path, anyOf(startsWith("https:"), is(nullValue())));
I changed the second matcher as I think you want the check if your String is null and not if it contains the String value "null".
i want to validate a ID.ID look like this POLxxxx. here ID can contain POL or MAN or NAY. x must be 0 or 1.as a example,
POL0111 is correct.
MAN0011 is correct.
NAY0111 is correct.
SAQ0111 is incorrect.
MAN0221 is incorrect.
below is my tried code
boolean x=l.matches("(?:POL|MAN|NEY)\\d[0,1]");
i want to get true if this is correct.bit i get false to POL1101.help me to solve this.thank you.
Tried this : (MAY|NAY|POL)[01]{4}
See demo