RestAssured assertThat, wildcard for key - rest-assured

I have an object like this,
{
"john": {
"number": "123"
},
"sarah": {
"number": "123"
}
}
It is an object where a persons name is the key for an object, like a map.
In restAssured how can I test for a ValidatableResponse that any number whether it belongs to john or sarah matches a certain value. In this case I know there will only be one person but an "any" matcher seems appepriate here.
I have tried assertThat().body("*.number"), Matchers.is("myValue)" but it does not work and gives an error

Your problem is extracting value from a json with dynamic key (the person name, in this case). There are NO built-in functions in Rest-Assured can help you.
To solve it
you can use JsonPath jayway to extract list of number by using deep-scan feature
then use hasItem assertion of Hamcrest.
import com.jayway.jsonpath.JsonPath;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;
#Test
void name() {
String json = "{\n" +
" \"john\": {\n" +
" \"number\": \"123\"\n" +
" },\n" +
" \"sarah\": {\n" +
" \"number\": \"123\"\n" +
" }\n" +
"}";
List<String> numbers = JsonPath.read(json, "$..number");
assertThat(numbers, hasItem("123"));
}
Note:
Please don't confused 2 JsonPath classes here. One is in Rest-Assured, one is in JsonPath jayway.
I have to say that I hate the json structure like this, it's better if using array to group items.
[
{
"name": "john",
"number": "123"
},
{
"name": "sarah",
"number": "123"
}
]

Related

How to validate response in one attempt

I want to test the response for POST request to petstore.swagger.io. I receive a response, why cannot I validate the body with path "id"? I always got errors, but regex is right and tested.
Test method:
#Test
public void postPet() {
Pattern pt = Pattern.compile("<(\\d*)>");
Response response = given()
.contentType("application/json")
.body(jsonObject)
.when()
.post(String.format("https://petstore.swagger.io/v2/pet"))
.then()
.statusCode(200)
.log().body()
.and()
.assertThat()
.body("id", matchesPattern(pt))
Error:
java.lang.AssertionError: 1 expectation failed.
JSON path id doesn't match.
Expected: a string matching the pattern '<(\d*)>'
Actual: <9223372000666122518L>
The response body is:
{
"id": 9223372000666122443,
"category": {
"id": 0,
"name": "string"
},
"name": "doggie",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "available"
}
And what is the L at the end of the id string? In swagger, there is no L as well as in the Postman. I tried regex without "< , >".
It's kind of clear that
your id is 9223372000666122518L, L means long (data type)
you use regex, but it only works for String
--> type mistmach when comparing long vs String
Solution:
Convert id to String first then assert
long id = ...log().body()
.extract().path("id");
assertThat(String.valueOf(id), Matchers.matchesPattern("\\d+"));
or
Write a custom Matcher, to check isLong, like this Is there a way in Hamcrest to test for a value to be a number?

spring data neo4j 6 #Query #Param can't access 2nd level children

I've a single object in the #Param to be used in #Query whereas the 1st level fields can be accessed without issues, but not the 2nd level. Unable to access fields from Language object
Consider:
{
"id": 0,
"description": "some text",
"language": {
"name": "English"
}
}
#RelationshipProperties
#Data
public class KnowsEntity {
#Id
#GeneratedValue()
private Long id;
#Property
private String description;
#TargetNode
private Language language;
}
#Query("MATCH (y:PERSON {nid: $from})-[r:KNOWS]->(e:LANGUAGE {name: $rel.__language__.__name__ }) \n" +
"WHERE ID(r) = $rel. id SET r.description = $rel.__description__ \n" +
"return y, collect(r), collect(e)")
UpdatedLanguageProjection updateRel(#Param("from") String from, #Param("rel") KnowsEntity relation);
form the above query i could access the first level field $rel.__description__ and not the field inside first level object $rel.__language__.__name__ (2nd level)
can someone please correct / suggest me an option to get this?
I got this worked with the following SpEL way
https://docs.spring.io/spring-data/neo4j/docs/6.1.1/reference/html/#custom-queries.spel
eg: {name: :#{#rel.language.name}}

Restassured: How Can we compare each element in Json array to one particular Same value in Java using Hemcrest Matchers, not using Foreach loop

Restassured: How Can we compare each element in Json array to one particular Same value in Java using Hemcrest Matchers, not using Foreach loop.
{
"id": 52352,
"name": "Great Apartments",
"floorplans": [
{
"id": 5342622,
"name": "THE STUDIO",
"fpCustomAmenities": [
{
"displaySequence": 2,
"amenityPartnerId": "gadasd",
"display": true,
"leased": true
},
{
"displaySequence": 13,
"amenityPartnerId": "sdfsfd",
"display": true,
"leased": true
}
]
},
{
"id": 4321020,
"name": "THE First Bed",
"fpCustomAmenities": [
{
"displaySequence": 4,
"amenityPartnerId": "gadasd",
"display": true,
"leased": true
},
{
"displaySequence": 15,
"amenityPartnerId": "hsfdsdf",
"display": true,
"leased": true
}
]
}
]
}
I want to compare that Leased=true for all the leased nodes at all the levels in the json response...
I have working code...
List<List<Boolean>> displayedvaluesfpStandardAmenities =
when().get(baseUrl + restUrl).
then().statusCode(200).log().ifError().
extract().body().jsonPath().getList("floorplans.fpCustomAmenities.display");
for (List<Boolean> displayedStandardList : displayedvaluesfpStandardAmenities) {
for (Boolean isDisplayedTrue : displayedStandardList) {
softAssert.assertTrue(isDisplayedTrue);
}
}
But the issue is I need the code to be in simple format using either Hemcrest Matchers or Restaussred Matchers and try simplistic way like Below, ( which is not working)
when().get(baseUrl + restUrl).
then().assertThat().body("floorplans.fpCustomAmenities.display",equalTo("true"));
The error I am getting is
java.lang.AssertionError: 1 expectation failed.
JSON path floorplans.fpCustomAmenities.display doesn't match.
Expected: true
Actual: <[[true, true], [true, true]]>
So what I need is the that all thes 'display' nodes in the json response where ever it is need to compared with "true", so that my test can Pass.
I have an alternate solution like mentioned above, but All I need is working solution using matchers.
Assuming fpCustomAmenities arrays are not empty, you can use the following solution;
when().get(baseUrl + restUrl).then()
.body("floorplans.findAll { it }.fpCustomAmenities" + // 1st line
".findAll { it }.leased.each{ a -> println a }" + // 2nd line
".grep{ it.contains(false) }.size()", equalTo(0)); // 3rd line
Here from the 1st line, we return each object in fpCustomAmenities array.
From the 2nd line we get boolean value of leased in each fpCustomAmenities object to a boolean array ([true, true]).
Each boolean array is printed from .each{ a -> println a }. I added it only to explain the answer. It is not relevant to the solution.
From 3rd line we check whether, if there is a false in each boolean array. grep() will return only the arrays which has a false. And then we get the filtered array count. Then we check whether it is equal to 0.
Check groovy documentation for more details.
Or
This solution does not use any Matchers. But this works.
String responseBody = when().get(baseUrl + restUrl).
then().extract().response().getBody().asPrettyString();
Assert.assertFalse(responseBody.contains("\"leased\": false"));

dymamic Schemas and nested Maps in Avro

I'm new to Avro, and am trying to write some code to serialize some nested objects.
The structure of the objects looks like this:
class Parcel {
String recipe;
Map<Integer, PluginDump> dumps;
}
class PluginDump {
byte[] state;
Map<String, Param> params;
}
class Param {
Type type; //can be e.g. StringType, BooleanType, etc
Object value;
}
So I can't use a static avro schema - each PluginDump will have a different schema depending on the types within it.
I have written some code which can generate a Schema based on an individual PluginDump.
So when serializing a Parcel, how do I 'put' each PluginDump entry?
Here is my code:
Schema parcelSchema = AvroHelper.getSchema(p);
GenericRecord parcelRecord = new GenericData.Record(parcelSchema);
parcelRecord.put("recipe", p.getRecipe().toJson());
for (Map.Entry<Integer, PluginDump> entry : p.getDumps().entrySet()) {
PluginDump dump = entry.getValue();
Integer uid = entry.getKey();
Schema dumpSchema = AvroHelper.getSchema(dump);//will be different for each PluginDump
parcelRecord.put(????
Any ideas?
I have a feeling my approach is wrong, but I can't find any examples in the documentation of dynamic schema generation or nested maps.
1 When you get GenericRecord parcelRecord = new GenericData.Record(parcelSchema); you have two fields in your record: recipe and dumps, so you can't iterate through the dumps, you must put prepared map with dumps in the second field of record, just like you did it for recipe: parcelRecord.put("dumps", dumps);. But in this case, you'll get ClassCastException, because PluginDump cannot be cast to org.apache.avro.generic.IndexedRecord, so you need to put in parcelRecord a Map of GenericRecords. Also you need this for Map<String, Param> params, cause Param cannot be cast to IndexedRecord too.
2 Then, I think that its better to use Lists instead of Maps, cause avro not very good enough to work with Maps with different types of keys and values.
3 About the Param class: if you will use auto-generated schema, Param class will be presented like this.
"type": "record",
"name": "Param",
"fields": [
{
"name": "type",
"type": {
"type": "record",
"name": "Type",
"namespace": "java.lang.reflect",
"fields": []
}
},
{
"name": "value",
"type": {
"type": "record",
"name": "Object",
"namespace": "java.lang",
"fields": []
}
}
]
As far as avro uses java.lang.reflect, you will lose type field after deserialization, avro will not know what type it was.
If you want to generate avro-schema manually for each Param, considering its type, you can do something like this (I used ClassUtils.getClass from apache commons-lang3, cause standart Class.forName method doesn't always work properly):
public Schema getParamSchema() throws ClassNotFoundException {
List<Schema.Field> fields = new ArrayList<>();
fields.add(new Schema.Field("key", Schema.create(Schema.Type.STRING), "Doc: key field", (Object) null));
Schema.Field f = new Schema.Field("type", ReflectData.get().getSchema(ClassUtils.getClass(((Class) this.type).getName())), "Doc: type field", (Object) null);
f.addProp("java-class", ((Class) this.type).getName());
fields.add(f);
fields.add(new Schema.Field("value", ReflectData.get().getSchema(value.getClass()), "Doc: value field", (Object) null));
return Schema.createRecord(((Class) this.type).getName() + "Param", "Doc: param record", this.getClass().getPackage().getName(), false, fields);
}
But in this case, avro will throw ClassCastException, because it can't cast Class to Boolean, Integer etc. I always had a lot of problems working with avro and java Types and Classes.
So the best advice i think will be to change you model (Parcel, PluginDump and Param i mean) to have less problems with avro. For example you can store type name like a string, and get a Type with reflection after deserializing.

Rest Assured Body handling ArrayList

I has a response body like this
enter code here
{
"applicationName": "Service MyService",
"someData": [
{
"name": "check1",
"props": [
"AAaa"
]
},
{
"name": "check2",
"props": [
"BBbb",
"CCcc"
]
}
]
}
Now I can use the following code and the test passes.
given().log().all()
.accept(JSON).expect().statusCode(SC_OK)
.when().log().all()
.get(contextPath + "/test")
.then().log().all()
.body("someData.name",
IsCollectionWithSize.hasSize(2))
.body("someData.name",
allOf(hasItems("check1", "check2")))
.body("someData.findAll {it.name == 'check1'}.props",
IsCollectionWithSize.hasSize(1))
.body("healthReports.findAll {it.name == 'check2'}.props",
IsCollectionWithSize.hasSize(2)));
However if I then attempt to check the values in the props field it fails I think because a ArrayList is returned and the matchers are checking on String.
given().log().all()
.accept(JSON).expect().statusCode(SC_OK)
.when().log().all()
.get(contextPath + "/test")
.then().log().all()
.body("someData.name",
IsCollectionWithSize.hasSize(2))
.body("healthReports.findAll {it.name == 'check1'}.props",
IsCollectionContaining.hasItems(startsWith("AA")));
I'm not sure how from the findAll ...props I can check the contents of the ArrayList.
The error displayed is:
JSON path someData.findAll {it.name == 'check1'}.props doesn't match.
Expected: (a collection containing a string starting with "AA")
Actual: [[AAaa]]
Any idea's ?
The findall return an Array of Array containing one element which is AA (which is why you have [[AAaa]] instead of [AAaa].
You have to flatten or extract one level of array to solve the problem I think.

Resources