How to verify that array contains object with rest assured? - java

For example, I have JSON in response:
[{"id":1,"name":"text"},{"id":2,"name":"text"}]}
I want to verify if a response contains a custom object. For example:
Person(id=1, name=text)
I found solution:
Person[] persons = response.as(Person[].class);
assertThat(person, IsArrayContaining.hasItemInArray(expectedPerson));
I want to have something like this:
response.then().assertThat().body(IsArrayContaining.hasItemInArray(object));
Is there any solution for this?
Thanks in advance for your help!

The body() method accepts a path and a Hamcrest matcher (see the javadocs).
So, you could do this:
response.then().assertThat().body("$", customMatcher);
For example:
// 'expected' is the serialised form of your Person
// this is a crude way of creating that serialised form
// you'll probably use whatever JSON de/serialisaiotn library is in use in your project
Map<String, Object> expected = new HashMap<String, Object>();
expected.put("id", 1);
expected.put("name", "text");
response.then().assertThat().body("$", Matchers.hasItem(expected));

This works for me:
body("path.to.array",
hasItem(
allOf(
hasEntry("firstName", "test"),
hasEntry("lastName", "test")
)
)
)

In this case, you can also use json schema validation. By using this we don't need to set individual rules for JSON elements.
Have a look at Rest assured schema validation
get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));

Related

WebClient does not return a "valid" list of Strings

I have a spring boot app that among others, has an endpoint that when hit, returns a list of strings. I also have another spring boot app that hits the first app's endpoint to get the data. The fetch code:
return webClient.get().uri("/sensors/get-cities").headers(httpHeaders -> {
httpHeaders.set("Authorization", auth);
}).retrieve()
.bodyToFlux(String.class).collectList().block();
The above yields a list but with this format when I inspect it in the debbuger, "["city"]". The outer double quotes, I get them because it's a string but the brackets and the inside double quotes, I do not. I tried replacing these characters but I had no luck with the brackets (tried regex). It is like they are not there, but at the same time they are. I am confused at this point. But I think that the behavior of the fetch code is not normal, it should yield a valid array of strings.
What you are probably getting (im guessing here) is a response body that looks something like this:
[
"New York",
"Madrid",
"London"
]
You then tell webflux that you want to convert the body to a Flux of String by calling bodyToFlux(String.class).
So the framework takes the entire response and makes a string out of it
// A string of the entire array (im escaping the quotation marks)
"[\"New York\",\"Madrid\",\"London\"]"
And then the framework will throw the entire thing into a Flux which means it takes the first position in the Flux. You then emit all the values into a List by calling collectList The equivalent code is sort of:
List<String> oneString = Flux.just("[\"New York\",\"Madrid\",\"London\"]")
.collectList()
.block();
So you get a list, with one string in it, which is the entire body.
What you probably want to do is to get a list out if it. And this is one way to do it:
List<String> strings = webClient.get()
.uri("/sensors/get-cities")
.headers(httpHeaders -> {
httpHeaders.set("Authorization", auth);
})
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<String>>() {})
.block();
Spring explains ParameterizedTypeReference:
The purpose of this class is to enable capturing and passing a generic Type. In order to capture the generic type and retain it at runtime
So its sort of a class that makes sure we can use generic types like List<T> and helps us with type information.
So what we do is that we now take the response and tell the framework that the body is a list of strings directly. We dont need to do collectList anymore as the framework will stick it in a list for us. We then call block to wait in the response.
Your Springboot API returns result as parsed to JSON (this is default behavior). So it first builds a list of Strings (in your case just a single String "city" and than serializes it to Json. In this case since it is a list it serializes it to JSON array as opposed to JSON Object. Read about JSON here. So in your second Springboot app that hits the API from the first one should assume that you are getting JSON which you need to parse to get your list. To parse it you can use readValue() method of ObjectMapper class of Json Jackson library which is a default JSON library in Springboot. your code would be
List<String> myList;
ObjectMapper = new ObjectMapper();
//Add setters for ObjectMapper configuration here if you want a specific config
try {
myList = objectMapper.readValue(myJsonString, List.class);
} catch(IOException ioe) {
...
}
In addition I wrote my own Open-source library called MgntUtils, that includes JsonUtils class which is a thin wrapper over Json Jackson library. It provides just Json parser and serializer, but in many cases that is all you need. With my library you would only need one dependency as oppose to Jackson, and JsonUtils class just have 4 methods, so by far easier to understand. But in your case if you use my library the code would be very similar to the above code. It would be something like this:
List<String> myList;
try {
myList = JsonUtils.readObjectFromJsonString(myJsonString, List.class);
} catch(IOException ioe) {
...
}
Note that in this case you won't have to instantiate and configure ObjectMapper instance as readObjectFromJsonString is a static method. Anyway if you are interested in using my library you can find maven artifacts here and The library itself with source code and javadoc is on Github here. Javadoc for JsonUtils class is here

AssertJ JSON property check

I have JSONObject instance which contains some property,
{
"name":"testName",
"age":"23"
}
i use the following assert, but it fails. Is this correct approach to test JSON in assertj.
assertThat(jsonObject).hasFieldOrProperty("name");
If you want to do any serious assertions on JSON object, I would recommend JsonUnit https://github.com/lukas-krecan/JsonUnit
I think it has to do with the fact the JSONObject is like a map which has key-value pairs, while AssertJ expects Java bean-style objects to check if a property exists. I understood this from the document at https://joel-costigliola.github.io/assertj/core/api/org/assertj/core/api/AbstractObjectAssert.html#hasFieldOrProperty(java.lang.String). Hope I am looking at the right place.
I mean to say that a map or JSONObject doesn't have fields declared in it for AssertJ to look for.
You may use JSONObject.has( String key ) instead, I think.
If you use SpringBoot you can use the custom impl. for Assertj
private final BasicJsonTester json = new BasicJsonTester(getClass());
#Test
void testIfHasPropertyName() {
final JSONObject jsonObject = new JSONObject("{\n" +
"\"name\":\"testName\",\n" +
"\"age\":\"23\"\n" +
"}");
assertThat(json.from(jsonObject.toString())).hasJsonPath( "$.name");
}
Fist, you need to traverse the keysets (nodes) using the map class, then verify if the keyset contains the particular node you are looking for.
Map<String, Object> read = JsonPath.read(JSONObject, "$");
assertThat(read.keySet()).contains("name");

Java POJO attributes mapping

I have a use case where I receive some attributes in the request like this,
"filters": [
{
"field": "fName",
"value": "Tom"
},
{
"field": "LName",
"value": "Hanks"
}
]
I don't have a model defined for this. I just receive these attributes in the request and fire a query on elastic search using these attributes. My records in elastic search have the same attribute names.
Now, I have to support a legacy application where attribute's names are completely different.
E.g.: fName becomes firstName and lName becomes lastName.
Problem: Need to accept old attribute names in the request, convert them to new ones so that it matches my elastic search records. Fetch the data with new attribute names and convert back to old ones before sending out the response from the application.
NOTE: I don't have POJO's defined for these records.
How can this be achieved effectively? I was thinking of using Orika mapper but not sure how that will work without defining classes first.
What prevents you from writing a transformer from request JSON to your normalized JSON?
The normal flow I can think of is:
Request JSON -> POJO -> POJO with normalized value -> Normalized JSON
So your POJO looks like:
public class Filter {
List<FieldFilter> filters;
public static class FieldFilter {
private String field;
private String value;
}
}
Now you will have a transformation map like:
Map<String, String> fieldNameMapping = new HashMap<>();
fieldNameMapping.put("fName", "firstName");
fieldNameMapping.put("firstName", "firstName");
// The process of populating this map can be done either by a static initializer, or config/properties reader
Then you transform your POJO:
Filter filterRequest;
List<FieldFilters> normlizedFilters =
filterReq.getFilters().stream()
.map(f -> new FieldFilter(fieldNameMapping.get(f.getField()), f.getValue())
.collect(toList());
Then convert the Filter class to your normalized JSON.
We have a similar scenario and we are using apache JOLT.If you want to try some samples, you can refer jolt-demo-online-utility
Use a JSON to JSON-transformer instead. Good answers regarding this can be found here: JSON to JSON transformer and here : XSLT equivalent for JSON
In the end you do not require an intermediate object type here. You even said, that you do not have such a type yet and inventing it, just to transform it, doesn't really make sense.

Filter single element from a multi value element in JSON

In my test I need to compare the expected and actual JSON response. But the JSON response is limited depending upon the role. So I need to exclude certain fields while comparing.
Below is the JSON and I want to filter out 2 things from it.
1. CompanyId
2. status.
{
userId=dg4d6g4dg45-rgdre-543-dfg,
userName=test123,
effectives=[
{
companyId=345634-54-547-74,
companyName=xyz,
roleId=685-345863490-634,
roleName=This is the test Role
},
{
companyId=345634-54-547-74,
companyName=xyz,
roleId=685-345863490-634,
roleName=This is the test Role
}
],
status=Active
}
Can you someone please let me know how to achieve this.
I explored the filterOutAllExcep method of SimpleBeanPropertyFilter but then I will have to figure out the logic to remember all the fields that should be included as well.
I think i have found a resolution for this.
I am using Object Mapper and converting the data model into a Map.
Then iterating over the map and removing the fields.
Flat filtering is straight forward but had to right up some logic for nested fields.
// Convert the DataModel into a Map object
ObjectMapper mapper = new ObjectMapper();
Map mainObjectMap = mapper.convertValue(object, Map.class);

Javascript Object to Java List

I have the following type of JSON I want to send to Java (I'm using Jersey and the default JSON Parser it comes with)
{ "something" : "1", "someOtherThing" : "2" , ... }
But instead of creating an Object with all these properties in Java, I would like to have a Single HashMap (or whatever) that will allow me to still have access to the Key and the Value
Is such a thing possible?
I don't really have any code that does the transformation, I use Jersey like this
#POST
#Path("/purchase")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public StatusResult purchase(UserPurchaseRequest upr) {
}
If i put properties something and someOtherThing as Strings in my UserPurchaseRequest object, everything will come in fine, but I want to have everything in one structure (because I don't know how many values I will get, and I need their names as well)
Yes, it is possible. But still, it depends on what JSON java API you are using. For example using Jackson JSON you can create HashMap json string like this
ObjectMapper obj = new ObjectMapper();
String json = pbj.writeValue(<HashMap object>);
or vice-versa
HashMap obj = obj.readValue(json, HashMap.class);
Note - org.codehaus.jackson.map.ObjectMapper
You just need to add a Property to your Object like this
private HashMap<String,String> purchaseValues;
Jersey takes care of the rest, for some reason while you are debugging, most of the entries appear as null in the HashMap

Categories