Rest API Testing || Quick Guide to RestAssured tutorial 2020

Rest Assured API testing for Rest API Testing automation

RestAssured -The rest assured tutorial api testing
Rest Assured API Automation

In this exhaustive Rest Assured tutorial we are going to learn the Rest API Testing in depth, API Test Automation along with Rest Assured in modularised approach

What is RestAssured and its use

Rest Assured is a very widely used open source technology for REST API Automation Testing , this is based on java based library.

Rest Assured interacts with Rest API in a headless client mode, we can enhance the same request by adding different layers to form the request and create HTTP request via different HTTPS verbs to the server .

Rest Assured built in library provides enormous methods and utilities to perform the validation of the response received from the server such as status message, status code and response body .

This complete series of Rest Assured Tutorial for REST API Automation Testing consists of the following topics :

Getting started: Configuration of restAssured with Build tool ie Maven/gradle

STEP 1 : If you are working with maven just add the following dependency in pom.xml (you can choose any other version as well):

To get started with REST Assured, just add the dependency to your project. 

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>4.3.0</version>
    <scope>test</scope>
</dependency>

If you are working with gradle just add the following in build.gradle (again you can choose any other version as well):

testCompile group: 'io.rest-assured', name: 'rest-assured', version: '4.3.0'

STEP 2 : REST Assured can be integrated and used very easily with the existing unit test frameworks ie Testng,JUnit

Here we are using testNg as per the Unit Test Framework is concerned.

Once the libraries of Rest Assured is imported then we need to add the following static imports to our test classes:

import static io.restassured.RestAssured.*;

import static org.hamcrest.Matchers.*;

NOTE : For this upcoming learning purpose , we’ll test the Ergast Developer API, which can be found here. This API provides historical data related to Formula 1 races, drivers, circuits, etc .

Familiarity with the Syntax:

Rest Assured is supports BDD format(Gherkin Syntax) to write the test scripts ie in Given/When/Then/And format ,We are assuming that you have understanding on BDD/gherkin syntax , if not then we would suggest to spend 30 minutes of time to understand what is BDD(Gherkin Syntax) and how does it work and its very basic .

T-01 : Our 1st script which is basically validating number of circuits in F1 in 2017 using this API (http://ergast.com/api/f1/2017/circuits.json)

@Test(description = "Number Of Circuits In 2017 Season Should Be 20")
public void validatingNumberOfCircuits() {
   given().when().get("http://ergast.com/api/f1/2017/circuits.json").
           then().assertThat().body("MRData.CircuitTable.Circuits.circuitId", hasSize(20));
}

Rest API response validation :

1. Captures JSON response of the API request.

2. Queries for circuitId using GPath expression “MRData.CircuitTable.Circuits.circuitId”

3. Verifies the circuitId elements collection has the size of 20

Here we are  using Hamcrest matchers for various validation such as

  • equalTo() validates the equality
  • lessThan() and greaterThan() methods for comparative validation ,
  • hasItem() method is used to validate the presence of an element from a collection of elements .

There are various other methods which is useful to perform certain validation.

You can furthermore refer to the Hamcrest library documentation for a full list of matchers and methods.

Validating response Code :

given().when().get("http://ergast.com/api/f1/2017/circuits.json").then().assertThat().statusCode(200);

Validation of Content Type

given().when().get("http://ergast.com/api/f1/2017/circuits.json").then().assertThat().contentType(ContentType.JSON);

Validating header “Content-Length”

given().when().get("http://ergast.com/api/f1/2017/circuits.json").then().assertThat().header("Content-Length",equalTo("4551"));

Multiple Validation in a single Tests as (By using and() methods ) :

@Test(description = "Number Of Circuits In 2017 Season Should Be 20")
    public void validatingNumberOfCircuits() {
        given().when().get("http://ergast.com/api/f1/2017/circuits.json").then().assertThat().header("Content-Length",equalTo("4551")).and().statusCode(200);
    }

Validating Response Body element/attribute :

We can use JsonPath to fetch the value of the json attributes and put assertion using TestNg

@Test(description = "Validation of series Name which is f1")
    public void validatingSeriesName() {
        //Convert ResponseBody to String
        String responseBody=given().when().get("http://ergast.com/api/f1/2017/circuits.json").getBody().asString();
        //Create JsonPath Object by Passing the Response Body as a string
        JsonPath resJson=new JsonPath(responseBody);
        //Fetch the attribute value series under MRData
        String seriesName=resJson.getString("MRData.series");
        // User TestNg Assertion
        Assert.assertEquals("f1",seriesName);
    }

In the similar fashion we could get the value of XML response using XMLPath .Here we are working with JSON hence we used here JSonPath

RESTful APIs support only two types of parameters:

A. Query parameters: Here parameters are appended at the end of the API endpoint and could be identified by the question mark and forms a key value pair such as 

https://www.google.com/search?q=https://www.wikipedia.org/

Here in the above API ‘q’ is the parameter and ‘https://www.wikipedia.org/’ is value of that parameter, if we to search ‘SOMETHING_ELSE_TEXT’ we could replace the value of the parameter ‘q’ with SOMETHING_ELSE_TEXT’ inplace of https://www.wikipedia.org/ .

B. Path parameters: These are the part of RESTful API endpoint. 

eg. endpoint that we used earlier: http://ergast.com/api/f1/2017/circuits.json, here “2017” is a path parameter value.

To get a result for the year 2016 we could replace 2017 with 2016 then the API will give the response body for 2016 .

Tests using Path Params for RestAssured

@Test(description = "Validation of number of Circuits using Path Params")
    public void testWithPathParams() {
        String seasonNumber = "2017";
       String responseBody = given().pathParam("season", seasonNumber).when().get("http://ergast.com/api/f1/{season}/circuits.json").getBody().asString();
        //Create JsonPath Object by Passing the Response Body as a string
        JsonPath resJson = new JsonPath(responseBody);
        //Fetch the attribute value series under MRData
        String seriesName = resJson.getString("MRData.series");
        // User TestNg Assertion
        Assert.assertEquals("f1", seriesName);
    }

Tests using Query Params for RestAssured

@Test(description = "Validation of Google search using Query Params")
    public void testWithPathParams() {
        String searchItem = "https://www.wikipedia.org/";
  given().queryParam("q",searchItem).when().get("https://www.google.com/search").then().assertThat().statusCode(200);
    }

Parameterizing tests:

We can do data driven testing (ie same test script will be executed multiple times with different sets of input data and provide different output data) using Rest Assured 

STEP 1 : Created a testNg Data Provider .

STEP 2 : Consume the Data Provider in Test script.

@DataProvider(name="seasonsAndRaceNumbers")
    public Object[][] testDataFeed() {
        return new Object[][] {
                {"2017",20},
                {"2016",21}
        };
    }
@Test(description = "Number Of Circuits validation in different Seasons",dataProvider = "seasonsAndRaceNumbers")
    public void circuitNumberValidation(String seasonYear,int raceNumbers) {
given().pathParam("season",seasonYear).when().get("http://ergast.com/api/f1/{season}/circuits.json").then().assertThat().body("MRData.CircuitTable.Circuits.circuitId",hasSize(raceNumbers));
    }

Working with Multi -valued parameters with RestAssured 

Multi-value parameters are those parameters which has more then one value per parameter name (i.e. a list of values per paramKey), we can address them like below :

given().param("paramKey", "paramValue1", "paramaValue2").when().get(“API URL“);

Or we could prepare a list and pass the list as the value of the paramKey like :

List<String>paramValue=new new ArrayList<String>();
paramValue.add(“paramvalue1”);
paramValue.add(“paramvalue2);
given().param("paramKey", paramValue).when().get(“API URL“);
Working with cookie with RestAssured 
given().cookie("cookieK", "cookieVal").when().get("API URL");

Or 

We can also specify a multi-value cookie here like :

given().cookie("cookieK", "cookieVal1", "cookieVal2").when().get(“API  URL”);

Working with Headers :

We can specify in a request using header/headers like :

given().header(“headerK1”,”headerValue1”).header(“headerK2”,”headerValue2”).when().get(“API URL”);

Working with contentType:

given().contentType("application/json").when().get(“API URL”);

Or 

given().contentType(ContentType.JSON).when().get();

Measure the Response Time :

long timeDurationInSeconds = get(“API URL”).timeIn(SECONDS);

Rest API Authentication

REST assured supports different auth schemes, eg OAuth, digest, certificate, form and preemptive basic authentication. We either can set authentication for each and every request 

here is a sample request using the same :

given().auth().basic("uName", "pwd").when().get(“URL “) ..

On the other hand authentication and defined in the below approach for the HTTP requests:

RestAssured.authentication = basic("uName", "pwd");

Basic AUTH Types:

There are two types of basic auth, “preemptive” and “challenged token basic authentication”.

Preemptive Basic Auth:

This will send the basic authentication credential even before the server gives an unauthorized response in certain situations along with the request being triggered, thus reducing the overhead of making an additional connection. This is typically majorly occurring situations unless we’re testing the servers ability to challenge. 

Eg.

given().auth().preemptive().basic("uName", "pwd").when().get("URL").then().statusCode(200);

Challenged Basic Authentication

On the Other hand “challenged basic authentication” REST Assured will not supply the credentials unless the server has explicitly asked for it i.e. server throws the Unauthorized Response. After that UnAuthorized response Rest-Assured sends another request to the server which is the Auth.

given().auth().basic("uName", "pwd").when().get("URL").then().statusCode(200);

Digest Authentication

As of now only “challenged digest authentication” is being considered. eg:

given().auth().digest("uName", "pwd").when().get("URL").then().statusCode(200); 

Form Authentication

We could achieve this majorly in 3 different approaches depending on the Application/Scenarios:

Form authentication is one of very popular over the internet which is an user is entering his credentials ie username & password through a web page and login in to the system.This Could be addressed using this 

given().auth().form("uName", "pWd").
when().get(" URL");
then().statusCode(200);

While this might not work as it’s optimal and it may pass or fail depending on the complexity of the webpage. A better option is to provide these details when setting up the form authentication in the below approach:

given().auth().form("uName", "pwd", new FormAuthConfig("/'mention here form action name which is part of the html page code nder the form tag'", "uName", "pwd")).when().get("URL").then().statusCode(200);

In this approach the REST Assured internally wont require to trigger additional request and parse through the webpage. 

If in case you are using the default Spring Security then a predefined FormAuthConfig is triggered .

given().auth().form("uName", "Pwd", FormAuthConfig.springSecurity()).when().get("URL").then().statusCode(200);

NOTE : If we want to send additional input data along with form auth then we could write the below:

given().auth().form("uName", "pwd", formAuthConfig().withAdditionalFields("firstInputField", "secondInputField"). ..

CSRF :

CSRF stands for Cross-site request forgery.

Nowadays it’s very common for the server to provide a CSRF token with the response to avoid the CSRF security attacks. REST Assured supports this by using and automatic parser and providing CSRF token . 

In order to achieve this REST Assured need to make an additional request and parse (few position)of the website.

We can enable CSRF support by writing the below code:

given().auth().form("uName", "pwd", formAuthConfig().withAutoDetectionOfCsrf()).when().get("URL").then().statusCode(200);

In Addition to assist REST Assured and make the parsing more flawless and robust we can supply the CSRF field name (here we assuming that we’re using Spring Security default values and we could use predefined springSecurity FormAuthConfig):

given().auth().form("uName", "pwd", springSecurity().withCsrfFieldName("_csrf")).when().get("URL").then().statusCode(200);

By default the CSRF value is passed as a form parameter with the request but we can configure to send it as a header if in case its required like below:

given().auth().form("uName", "pwd", springSecurity().withCsrfFieldName("_csrf").sendCsrfTokenAsHeader()).when().get("URL").then().statusCode(200);

OAuth 1 :

OAuth 1 requires Scribe to be in the classpath. To use oAuth 1 authentication we can do:

given().auth().oauth(..).when(). ..

OAuth 2 :

given().auth().oauth2(accessToken).when(). ..

In the above approach the OAuth2 accessToken will be considered in a header. To be more explicit we can also do:

given().auth().preemptive().oauth2(accessToken).when(). ..

Passing File, byte-array, input stream or text in Request:

When sending large amounts of data to the server it’s generally a common approach to use the multipart form data technique. Rest Assured provide methods called multiPart that allows us to specify a file, byte-array, input stream or text to upload. 

given().multiPart(new File("/File_Path")).when().post("/upload");

POST Request Creation with Rest Assured

With POST and PUT requests, we send Data to Server and its basically creation of resources/updation of resources, you can consider this as a write or update operation.

The data which is being sent to the server in a POST request is sent in the body of HTTP request/API call. 

The type of content or data which is being sent can be of different format depending on the API i.e. XML, JSON or some other format is defined by the Content-Type header. 

If POST body consists of the JSON data then the header Content-Type will be application/json.Similarly , for a POST request consisting of a XML then the header Content-Type would be of application/xml type.

Here is the below code snippet for the same:

given().contentType("application/json").param("pk","pv").when().body("JsonPAyloadString").post("url").then().assertThat().statusCode(200);

NOTE: There are different ways we can pass the payload/request body  inside the method “ body “ like String(as shown in above snippet),JsonObject,as a File etc etc,

PUT Request with Rest Assured:

given().contentType("application/json").param("pk","pv").when().body("JsonPAyloadString").put("url").then().assertThat().statusCode(200);

Delete request with Rest-Assured :

given().contentType("application/json").param("pk","pv").when().delete("url").then().assertThat().statusCode(200);

And that way we can create different Rest API call for different API verbs(GET/POST/PUT/DELETE etc)

Serialization and Deserialization in Java :

Serialization is a basically processing or converting the object state to a byte stream. On the other hand the Deserialization in Java is processing or converting the byte stream to actual Java object within memory . This mechanism is used in persistence of Object.

Below is the block diagram for the same 

Advantages of the Serialization

A. To save/persist the state of an object.

B. To flow an object across a network.

Achieving Serialization with JAVA

To achieve a Java object serializable we need to implement the java.io.Serializable interface.

The ObjectOutputStream class which contains writeObject() method responsible for serializing an Object.

The ObjectInputStream class also contains another method called readObject() which is responsible for deserializing an object.

classes which are implementing java.io.Serializable interface, there object can only be serialized.

Serializable is just a marker interface and like other market interface it has no data member or method associated with it.which is used to “mark” java classes so that objects of these classes will get certain capabilities. Like few other marker interfaces are:- Cloneable and Remote etc.

NOTEs :

1. If a parent class has implemented a Serializable interface then child class is not required to implement the same but vice-versa is not applicable.

2. Only non-static data members are stored with the Serialization process.

3. Static data members and also the transient data members are not being stored by the Serialization .So, in case if we dont need to store store the non-static data member’s value then we can make it transient.

4. Constructor is never called when an object is deserialized.

STEP 1 : The first step is basically the creation of a class which implements the Serializable interface:

import java.io.Serializable;
public class Dummy implements Serializable {
    private int i;
    private String  data;
    public Dummy(int i, String data)
    {
        this.i = i;
        this.data = data;
    }
}

STEP 2 :Create a class to serialize it :

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class  Serialize {
    public static void Serialization(Object classObject, String fileName) {
        try {
            FileOutputStream fileStream = new FileOutputStream(fileName);
            ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
            objectStream.writeObject(classObject);
            objectStream.close();
            fileStream.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Dummy dummyObj = new Dummy(10, "Lambda-geeks");
        Serialization(dummyObj, "DummSerialized");
    }
}

STEP 3 : Once Step2 is completed successfully then you would get to see a file got created with some data in it ,that data is basically serialized data of the Object members.

  Deserialization with java :

Here is the below code snippet :

 public static Object DeSerialize(String fileName)
    {
        try {
            FileInputStream fileStream = new FileInputStream(new File(fileName));
            ObjectInputStream objectStream = new ObjectInputStream(fileStream);
            Object deserializeObject = objectStream.readObject();
            objectStream.close();
            fileStream.close();
            return deserializeObject;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

Driver Code goes like this :

 public static void main(String[] args) {
      /*  Dummy dummyObj = new Dummy(10, "Lambda-geeks");
        Serialization(dummyObj, "DummSerialized");
        System.out.println("--------------------------------------------------------------------------");
      */
        Dummy deSerializedRect = (Dummy) DeSerialize("DummSerialized");
        System.out.println("Data From Serialized Object " + deSerializedRect.print());
        System.out.println("--------------------------------------------------------------------------");
    }

JSONPATH More Syntax/Query :

Lets Assume a JSON as below :

{
  "OrganizationDetails": "Dummy Details of the Organization",
  "Region": "Asia",
  "Emp-Details": [
    {
      "Org": "lambda-Geeks",
      "Information": {
        "Ph": 1234567890,
        "Add": "XYZ",
        "Age": 45
      }
    },
    {
      "Org": "lambda-Geeks-2",
      "Information": {
        "Ph": 2134561230,
        "Add": "ABC",
        "Age": 35
      }
    }
  ]
}

in the above JSON , the OrganizationDetails & Region are called as Leaf node reason being they are not having any further child nodes/elements but  as on the other hand the Emp-Details having child node , hence its not referred as Leaf node.

Here if we try get the value of  OrganizationDetails then we need to use :

$.OrganizationDetails 
This will result in :
 [
  "Dummy Details of the Organization"
]

Like Wise to get the data for region we need to write :

$.Region 

If we want to find the value of  Age for the 1st Employee then we could write :

$.Emp-Details[0].Information.Age
This will result in :
[
  45
]

For the Age of the 2nd Employee  we could write like

$.Emp-Details[1].Information.Age
This will result in : 
[
  35
]

This way we can figure out the JsonPath expression/query to fetch the data for the respective fields in the JSON.

About Debarghya

Myself Debarghya Roy, I am an Engineering ARCHITECT working with fortune 5 company and an open source contributor, having around 12 years of experience/expertise in various Technology stack.
I have worked with various technologies such as Java,C#,Python,Groovy, UI Automation(Selenium), Mobile Automation(Appium), API/Backend Automation,Performance Engineering(JMeter, Locust), Security Automation(MobSF,OwAsp,Kali Linux, Astra,ZAP etc), RPA,Process Engineering Automation,Mainframe Automation,Back End Development with SpringBoot,Kafka,Redis,RabitMQ,ELK stack, GrayLog, Jenkins and also having experience in Cloud Technologies, DevOps etc.
I live in Bangalore ,India with my wife and have passion towards Blogging , music , playing guitar and my Philosophy of life is Education for All which gave the birth of LambdaGeeks. Lets connect over linked-in - https://www.linkedin.com/in/debarghya-roy/