One of my activities is to review the Quality Engineers’ technical assignment. We ask the candidate to automate at least 2 features in the API and Web layers.
Most of the candidates are delivering the API test automation solution using Java and Rest-Assured, which is the same combination we use. Of course, we are evaluating it based on the candidate experience in general which includes the whole testing experience, test automation experience, and libraries experience.
Rest-Assured is a well-known library to use for API test automation, simple to use, and fast. But I can see one gap in most of the solutions they delivered: the lack of knowledge about the library. It’s in different ways and one is unanimous: the use of either String
or HashMap
in the requests.
The intention here is to tell you which way you can have most of the professional benefits using Rest-Assured as I refer to it here as the recommended approach.
To not add some complexity here and stay focused on our main goal we will use the https://reqres.in/, a mock API ready to respond to any request for testing purposes.
As the goal is sent a request with an object with it, sometimes referred to as the payload object, we will use the following endpoint:
Request
Path: /api/users
Request body (payload)
{
"name": "morpheus",
"job": "leader"
}
Response
Status code: 201
Response Body (example)
{
"name": "morpheus",
"job": "leader",
"id": "909",
"createdAt": "2021-03-20T16:02:52.694Z"
}
Given the request body, we must send the name
and job
attributes, which are text (strings).
The string concatenation approach consists of creating a string with the same output as JSON.
Note that, on line 5, the String
user is a string concatenation of the JSON Object we must send into the request.
As we are using Java we must escape all the double quotes, as a string starts and ends with it.
The validation on lines 15 and 16 must be hardcoded as well because there is no way to retrieve the data sent because it is a string.
We can see clearly that this approach works but will give you a lot of maintenance and trouble. You must avoid it.
Advantages
Disadvantages
You can see this approach described in the Rest-Assured documentation but it does not mean this is the best approach.
This approach consists of creating a HashMap
object to send it on the request.
As a HashMap
as a key-value map where the key is the attribute and value is the attribute value, it seems a good candidate to use.
In line 5 we are using a Map
typing the attribute as String and the value as an Object
. This is not the case but you know that a JSON value can be an object.
On lines 6 and 7 we are adding the key (attribute name) and value (the value we want to send).
Behind the scene the binding library will transform the HashMap
into a JSON object like this:
{ "name": "Elias", "job": "Principal Engineer" }
This approach is better than the String concatenation and looks more professional, but it’s not the best one…
Advantages
Disadvantages
HashMap
put("name", "New value")
put("name", "Elias")
as we have a typo on “o”Rest-Assured supports mapping Java objects to and from JSON and XML. This means you can create a Java object and Rest-Assured will try to match all the attributes in the class with either the request or response.
To be able to use it you need to explicitly add an object mapping library in your classpath. For JSON you need to have either Jackson, Jackson2, Gson, or Johnzon in the classpath and for XML you need JAXB.
As we are sending information into the request let’s talk about first the Serialization.
https://github.com/rest-assured/rest-assured/wiki/Usage#serialization
Rest-Assured will use one of the object mapping libraries you added to your classpath to bind the attributes from a Java object into the request body. We already know that we required, for this example, the name
and job
attributes to post an user. The Java object will look like this:
In the test, we need first to create the User
object with the data we would like to use. You can see this on line 6.
Now we simply use the user
object into the body(object). It’s important to explicitly set the Content-type. You can see it in lines 9 and 10.
Rest-Assured will do the magic when the body()
method uses an object where it will try to find the required request attribute names in the object used.
The request needs the name
and job
attributes, as text. As the User
object has the name
and job attributes as String (text) RestAssured will to the automatic value match. Both combinations of attribute and value match are called Object Serialization.
You might face the following exception, which is super self-explanatory:
java.lang.IllegalStateException: Cannot serialize object because no JSON serializer found in the classpath. Please put Jackson (Databind), Gson, Johnzon, or Yasson in the classpath.
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:72)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:59)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:263)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:277)
at io.restassured.internal.mapping.ObjectMapping.serialize(ObjectMapping.groovy:160)
at io.restassured.internal.mapping.ObjectMapping$serialize.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at io.restassured.internal.RequestSpecificationImpl.body(RequestSpecificationImpl.groovy:751)
This will happen if you don’t add any library to serialize the objects. You need to explicitly add one of the following libraries:
It might be the case you are not using one of these libraries, having a successful test execution. Some dependencies can have one of these as an internal dependency, and you can figure out it simply by running mvn dependency:tree
and trying to look at one of the libraries mentioned.
In the restassured-complete-basic-example project, you can find the Simulation model, which models the request and response for the Simulations API.
The model is in use by the SimulaitonDataFactory to create the Simulation
data, necessary to make the POST
request using the body parameter.
The SimulationFunctionalTest has tests using different HTTP methods. You can see the createNewSimulationSuccessfully, invalidSimulations, and simulationWithDuplicatedCpf test methods using the Simulation
object to POST
a body using it instead of Strings or any other mentioned approach.
2 Comments
Hi Elias,
Thank you for this great blog. What do you recommend for Typescript/Javascript API resting? There is no library like rest assured.
Thanks
Hi Omer,
Unfortunately, I don’t know very well the JS/TS tools but I saw people using frisby.js