Dealing arrays with hamcrest and rest assured - hamcrest

I can't figure out how to create the code using hamcrest to check an array inside array having these properties.
(Imagine this as it has multiple entries with different data)
{
"mobilenum": "+6519829340",
"firstname": "Allen",
"lastname": "Edwards",
"location": "Singapore"
}
If I use this:
.body("smsentries.mobilenum", contains(equalTo("+6519829340")));
it returns that it does exist but how can I put more checks that the object it has found also has the same firstname, lastname and location?
I also think that this is wrong:
.body("smsentries.mobilenum", contains(equalTo("+6519829340")))
.and()
.body("smsentries.firstname", contains(equalTo("Allen"));
As what I understand here is that it searches the array if the array contains mobilenum equal to what is provided and if the array contains the name "Allen"
What I needed to is to find the array having the mobilenum equal to "+6519829340" and having the firstname equalto "Allen".
Do you guys and gals have any idea how to go about this?

What I needed to is to find the array having the mobilenum equal to
"+6519829340" and having the firstname equalto "Allen".
You can make use of the "find" method:
.body("smsentries.find { it.mobilenum == '+6519829340' }.firstname", equalTo("Allen")
.body("smsentries.find { it.mobilenum == '+6519829340' }.lastname", equalTo("Edwards").
As you see you're essentially duplicating the path expression in the two cases so to improve this we can make use of root paths:
.root("smsentries.find { it.mobilenum == '+6519829340' }").
.body("firstname", equalTo("Allen")
.body("lastname", equalTo("Edwards").
You can also parameterize root paths:
.root("smsentries.find { it.mobilenum == '%s' }").
.body("firstname", withArgs("+6519829340"), equalTo("Allen")
.body("lastname", withArgs("+6519829340"), equalTo("Edwards").
.body("firstname", withArgs("+12345678"), equalTo("John")
.body("lastname", withArgs("+12345678"), equalTo("Doe").

Related

iOS Swit 3 - filter array inside filter

I would like to filter array inside a filter. First I have a big array of Staff object (self.bookingSettings.staffs). Inside this array I have multiple object like this :
"staffs": [
{
"id": 1,
"name": "Brian",
"services": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4
}
],
"pos": 1
},...
I would like to filter this array in order to have only services with id = 3.
I succeed to have if first object is equal to 3 with this code :
self.bookingSettings.staffs.filter({ $0.services.first?.id == self.bookingService.id })
but that takes only the first item.
I think I have to filter inside my filter function, something like this to loop over all object inside services :
self.bookingSettings.staffs.filter({ $0.services.filter({ $0.id == self.bookingService.id }) })
but I've the following error: Cannot convert value of type [BookingService] to closure result type Bool.
Is this a good idea ? How can I achieve this ?
You could use filter, which would look something like this:
self.bookingSettings.staffs.filter {
!$0.services.filter{ $0.id == self.bookingService.id }.isEmpty
}
This code is constructing an entire array of filtered results, only to check if its empty and immediately discard it. Since filter returns all items that match the predicate from the list, it won't stop after it finds a match (which is really what you're looking for). So even if the first element out of a list of a million elements matches, it'll still go on to check 999,999 more elements. If the other 999,999 elements also match, then they will all be copied into filter's result. That's silly, and can use way more CPU and RAM than necessary in this case.
You just need contains(where:):
self.bookingSettings.staffs.filter {
$0.services.contains(where: { $0.id == self.bookingService.id })
}
contains(where:) short-circuits, meaning that it won't keep checking elements after a match is found. It stops and returns true as soon as find a match. It also doesn't both copying matching elements into a new list.

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.

Modifying NSMutableDictionary by key path

I have a NSDictionary with nested NSDictionary/NSArray (Generated through a JSON).
I need to remove objects in multiple places according to some logic imposed on me.
For instance lets say that the dictionary has some structure like:
{
"Array1" : [
{
"InnerArray1" : [
{
"some conditional field" : Evaluate this ...
}
],
"More Properties : ....
}
],
"Array2" : {
... some complex inner structure
{
// some inner object to remove according to "evaluate condition"
}
}
}
When I iterate the "Array1.InnerArray1..." and evaluate some condition to true , I need to remove objects in a disjoint location.
Ideally I could do something like
foreach item in Array1.InnerArray1 {
if item evaluates to true {
// evaluate the disjoint location...
remove Array2.....InnerObject(s) where ConditionField == true
}
}
Currently it is very awkward to iterate the inner objects and mutate them on the same pass (and very hard to read).
Given I have created a deep mutable copy of this whole data tree, is it possible to remove nested objects using kvc or another mechanism without nested iterations?

Grails gorm 'where' query with 'lower()' and 'in' operator

I have a Domain class
class Hashtag {
String tag
}
Why
Hashtag.where { lower(tag) == "#london" }.list()
works ok, but
Hashtag.where { lower(tag) in [ "#london", "#paris" ] }.list()
results in
org.springframework.dao.InvalidDataAccessResourceUsageException: Unsupported function [lower] defined in query for property [hashtag] with type [class java.lang.String]
How to write such query properly?
Thanks!
I can't answer why using lower() with in() did not work. I read the source but I don't know ASTs well enough to understand it.
But, to solve your problem you can use a derived property to perform the lower().
class Hashtag {
String tag
String loweredTag
static mapping {
loweredTag formula: 'lower(tag)'
}
}
Then you can use the derived property in the where query:
Hashtag.where { loweredTag in [ "#london", "#paris" ] }.list()
Answering my own question, I've found alternative way to do case-insensitive comparison - using Criteria. An advantage of this method is that values in cities also could be mixed cases.
def cities = ["#LONdon", "#PAris"]
Hashtag.createCriteria().list {
or {
cities.each { eq("tag", it, [ignoreCase: true])
}
}
I'm not sure if it will work as per your need.
But documentation says that you may use subqueries in where block like below:
Hashtag.where { lower(tag).of{} in [ "#london", "#paris" ] }.list()
Please try it and let me know if it doesn't work.
Hope it helps!

JSON arrays with duplicate items with Hamcrest and RestAssured

As a follow up question for my question in Dealing arrays with hamcrest and rest assured
How can I use hamcrest with restassured so that I can test
{
"mobilenum": "+6519829340",
"firstname": "Allen",
"lastname": "Edwards",
"location": "Singapore"
"outbound": "YES"
"count" : 15
},
{
"mobilenum": "+6519829340",
"firstname": "Allen",
"lastname": "Edwards",
"location": "Singapore"
"outbound": "NO"
"count" : 9
}
That there exist two types of data, one containing mobilenum, firstname, etc having outbound equal to yes, and the other no.
It would be like having two objects having the same properties except the outbound property.
An answer by John, from the previous question is this:
.root("smsentries.find { it.mobilenum == '%s' }").
.body("firstname", withArgs("+6519829340"), equalTo("Allen")
.body("lastname", withArgs("+6519829340"), equalTo("Edwards").
.body("firstname", withArgs("+12345678"), equalTo("John")
.body("lastname", withArgs("+12345678"), equalTo("Doe").
I don't know how to add something like withArgs("Allen") and ("Edwards) .equalTo("outbound")
UPDATE
What I hope to happen is like this:
for (Map.Entry<String,JsonElement> entry : o.entrySet()) {
if (entry.getKey().equals("smsentries")) {
JsonArray array = entry.getValue().getAsJsonArray();
for (JsonElement elementJSON : array) {
SMSEntry smsEntry = mapper.readValue(elementJSON.toString(), SMSEntry.class);
if (smsEntry.getMobilenum().equals("+6519829340") &&
smsEntry.getOutbound().equals("YES")) {
assertThat(smsEntry.getLocation(), equalTo("Singapore"));
assertThat(smsEntry.getCount(), equalTo(15));
}
}
}
}
If I have a mobile number equal to +6519829340 and is outbound, assert that the location is in Singapore and has count of 15.
If I understand you correctly (and that the list of users(?) is called smsentries as it was in the previous question) you could do like this:
.root("smsentries.findAll { it.mobilenum == '%s' }").
.body("firstname", withArgs("+6519829340"), contains("Allen", "Allen"))
.body("lastname", withArgs("+6519829340"), contains("Edwards", "Edwards"))
.body("outbound", withArgs("+6519829340"), containsInAnyOrder("YES", "NO"))
// Additional body matchers
Update after clarification
If I have a mobile number equal to +6519829340 and is outbound, assert
that the location is in Singapore and has count of 15.
You can do like this:
.root("smsentries.find { it.mobilenum == '+6519829340' && it.outbound == 'YES'}").
.body("location", equalTo("Singapore"))
.body("count", equalTo(9))

Resources