Rest Assured Basics

Agenda

To cover basic concepts of rest-assured framework, that will enable us to achieve API automation.

Introduction

  1. API(Application Programming Interface) is an interface between client and server, which makes a request in specific format and gets response in a specific format.

Terminology

  1. Endpoint/base uri : Address where API is hosted
  2. GET, POST, PUT, DELETE --> common methods to interact with REST API's, often we call them as CRUD operations (Create, Retrieve, Update, Delete)
    1. GET --> used to extract data from server
    2. POST --> used to send data to server
    3. PUT --> to replace existing resources on server
    4. DELETE --> to delete a resource on server
  3. Resource --> more of a package under endpoint.
    1. google/maps, google/docs --> where maps and docs are resources.
  4. Parameters
    1. Path Parameters: points to specific resource within a collection
      1. https://google.com/images/123
    2. Query Parameters: used to sort or filter the resources
      1. https://amazon.com/orders?sort_by=4/28/2022
  5. API might look like endpoint/resource/(query/path parameters)
  6. Headers/Cookies --> it's more like meta data(additional data) associated API request and response.
    1. Example: Authorization details

Rest Assured

  1. have maven dependencies for rest-assured, hamcrest and testng into your project
<dependencies>
<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
</dependency>
</dependencies>

Scenario

Add place --> update place --> get place

Add place API

given() --> captures all request inputs
when() --> submits the api with resource
then() --> for any validations

Simple code to validate api response for 200 status code

Request:
Request method: POST
Request URI: https://name.com/maps/api/place/add/json?key=qaclick123
Proxy: <none>
Request params: <none>
Query params: key=qaclick123
Form params: <none>
Path params: <none>
Headers: Accept=*/*
Content-Type=application/json
Cookies: <none>
Multiparts: <none>
Body:
{
    "location": {
        "lat": -38.383494,
        "lng": 33.427362
    },
    "accuracy": 50,
    "name": "Frontline house",
    "phone_number": "(+91) 983 893 3937",
    "address": "29, side layout, cohen 09",
    "types": [
        "shoe park",
        "shop"
    ],
    "website": "http://google.com",
    "language": "French-IN"
}
Response:
{
    "status": "OK",
    "place_id": "c7fde72466f72961cd578baf63ae8a8f",
    "scope": "APP",
    "reference": "ca17b2d14af2ac425711cf37a61078feca17b2d14af2ac425711cf37a61078fe",
    "id": "ca17b2d14af2ac425711cf37a61078fe"
}

Code:
public void testAddPlace(){
RestAssured.baseURI = "https://name.com";
given().log().all().queryParam("key", "qaclick123").header("Content-Type", "application/json")
.body("{\n" +
" \"location\": {\n" +
" \"lat\": -38.383494,\n" +
" \"lng\": 33.427362\n" +
" },\n" +
" \"accuracy\": 50,\n" +
" \"name\": \"Frontline house\",\n" +
" \"phone_number\": \"(+91) 983 893 3937\",\n" +
" \"address\": \"29, side layout, cohen 09\",\n" +
" \"types\": [\n" +
" \"shoe park\",\n" +
" \"shop\"\n" +
" ],\n" +
" \"website\": \"http://google.com\",\n" +
" \"language\": \"French-IN\"\n" +
"}").when().post("maps/api/place/add/json")
.then().log().all().assertThat().statusCode(200);
}

Lets add assertions to above code

.then().log().all().assertThat().statusCode(200)
.body("scope", equalTo("APP"))
.header("Server", "Apache/2.4.41 (Ubuntu)");

Extract data

.extract().response().asString();
use a variable to capture response.

Parse json using JsonPath class and extract place

{
    "status""OK",
    "place_id""201e0f6b1fa6a2cc7500c9b2df199898",
    "scope""APP",
    "reference""35a4897fa87e3db6f0f8017a7faf63a835a4897fa87e3db6f0f8017a7faf63a8",
    "id""35a4897fa87e3db6f0f8017a7faf63a8"
}
JsonPath jsonPath = new JsonPath(response);
String place = jsonPath.getString("place_id");

update place

Request:
Request method: PUT
Request URI: https://name.com/maps/api/place/update/json?key=qaclick123
Proxy: <none>
Request params: <none>
Query params: key=qaclick123
Form params: <none>
Path params: <none>
Headers: Accept=*/*
Content-Type=application/json
Cookies: <none>
Multiparts: <none>
Body:
{
    "place_id": "c7fde72466f72961cd578baf63ae8a8f",
    "address": "70 winter walk, USA",
    "key": "qaclick123"
}
Response:
{
    "msg": "Address successfully updated"
}

given().log().all().queryParam("key", "qaclick123").header("Content-Type", "application/json")
.body("{\n" +
"\"place_id\":\""+place+"\",\n" +
"\"address\":\"70 winter walk, USA\",\n" +
"\"key\":\"qaclick123\"\n" +
"}").when().put("maps/api/place/update/json")
.then().log().all().assertThat().statusCode(200)
.body("msg", equalTo("Address successfully updated"));

Get place and validate address

String getResponse = given().log().all().queryParam("key", "qaclick123").queryParam("place_id", place)
.when().get("maps/api/place/get/json")
.then().log().all().assertThat().statusCode(200)
.extract().response().asString();
jsonPath = new JsonPath(getResponse);
String currentAddress = jsonPath.getString("address");
Assert.assertTrue(currentAddress.equalsIgnoreCase(newAddress), "Address mismatch");

Traversing json

Lets take a look at complex json

{
    "dashboard": {
        "purchaseAmount": 1162,
        "website": "name.com"
    },
    "courses": [
        {
            "title": "Selenium Python",
            "price": 50,
            "copies": 6
        },
        {
            "title": "Cypress",
            "price": 40,
            "copies": 4
        },
        {
            "title": "RPA",
            "price": 45,
            "copies": 10
        },
        {
            "title": "Appium",
            "price": 36,
            "copies": 7
        }
    ]
}

get Number of courses

String count = jsonPath.getString("courses.size()");
System.out.println("course size : "+count);

print purchase amount

String purchaseAmount = jsonPath.getString("dashboard.purchaseAmount");
System.out.println("Purchase amount is "+purchaseAmount);

get title of first course

String title = jsonPath.getString("courses[0].title");
System.out.println("title of course is : "+title);

print all courses and their respective titles

for(int i=0; i<count; i++){
System.out.println("title of course is : "+jsonPath.getString("courses["+i+"].title"));
}

Parse json file to string

Read all the bytes and convert to a string
System.out.println(new String(Files.readAllBytes(Paths.get(System.getProperty("user.dir")+"//src/test//resources//complex.json"))));

Cookie based Authentication

  1. invoke an endpoint that creates a session id with your login credentials
  2. this session id, will be used as a cookie in header parameters for any request

Adding path parameter

endpoint: https://localhost:8080/slab/id/{125}/price

observe key is parameterized

given().pathParams("key", "123").log().all().header("Content-Type", "application/json")
.body(PayLoads.getAddPlacePayload()).when().post("slabs/id/{key}/price")

Capturing session using session filter

  1. one of way capturing session from the response is by using jsonpath and get the session value
  2. other way, is by using SessionFilter class and using filter method, which will have the knowledge of session.
given().pathParams("key", "123").log().all().header("Content-Type", "application/json")
.body(PayLoads.getAddPlacePayload()).filter(sessionFilter).when().post("slabs/id/{key}/price")

file attachment using multipart

observe how header and multipart methods are configured
given().pathParams("key", "123").log().all().
header("Content-Type", "multipart/form-data")
.body(PayLoads.getAddPlacePayload()).filter(sessionFilter).
multiPart("file", new File("java.txt")).when().post("slabs/id/{key}/price")

url encoding false

if there are special characters in your url, to handle it use below command
given().urlEncodingEnabled(false)

https certificate issue

we can relax these issues by
given().relaxedHTTPSValidation().

OAuth2.0 Authorization

  1. comes with multiple grant types, most commonly used are Authorization code and client credentials.

Authorization code grant type

details that are needed from client
client id, client secret, resource owner, resource/authorization server
Ex: bookmyshow, client id, client secret, me, Google

in detail, 
  1. client sends client id and secret to authorization server and gets authorization code.
  2. using this authorization code, call will be invoked to resource server to get access code and other details
  3. this access token will be used for all calls.
Note: scope of token can be increased

client credentials grant type

here Application request for client credentials from resource server
Example: integrating twitter into your website

Note: In postman, there is a option to generate new token in OAuth2.0 mechanism, you can check for your reference.

Serializing and de-serializing json

have the dependencies downloaded
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
</dependency>

Lets de-serialize

Runner class
GetPlace getPlace = given().log().all().queryParam("key", "qaclick123").
queryParam("place_id", place).expect().defaultParser(Parser.JSON)
.when().get("maps/api/place/get/json")
.then().log().all().assertThat().statusCode(200)
.extract().as(GetPlace.class);

System.out.println(getPlace.getAccuracy());
GetPlace class
package day3;

public class GetPlace {

private Location location;
private String accuracy;
private String name;
private String phone_number;
private String address;
private String types;
private String website;
private String language;

public Location getLocation() {
return location;
}

public void setLocation(Location location) {
this.location = location;
}

public String getAccuracy() {
return accuracy;
}

public void setAccuracy(String accuracy) {
this.accuracy = accuracy;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPhone_number() {
return phone_number;
}

public void setPhone_number(String phone_number) {
this.phone_number = phone_number;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getTypes() {
return types;
}

public void setTypes(String types) {
this.types = types;
}

public String getWebsite() {
return website;
}

public void setWebsite(String website) {
this.website = website;
}

public String getLanguage() {
return language;
}

public void setLanguage(String language) {
this.language = language;
}
}
Location class
package day3;

public class Location {
private String latitude;
private String longitude;

public String getLatitude() {
return latitude;
}

public void setLatitude(String latitude) {
this.latitude = latitude;
}

public String getLongitude() {
return longitude;
}

public void setLongitude(String longitude) {
this.longitude = longitude;
}
}
json
{
"location": {
"latitude": "-38.383494",
"longitude": "33.427362"
},
"accuracy": "50",
"name": "Frontline house",
"phone_number": "(+91) 983 893 3937",
"address": "29, side layout, cohen 09",
"types": "shoe park,shop",
"website": "http://google.com",
"language": "French-IN"
}

Let's Serialize

driver class
AddPlace addPlace = new AddPlace();
addPlace.setAccuracy("50");
addPlace.setName("vinay");
addPlace.setPhone_number("(+91) 983 893 3937");
addPlace.setAddress("vinay, side layout, cohen 09");
List<String> types = new ArrayList<>();
types.add("shoe park");
types.add("shop");
addPlace.setTypes(types);
addPlace.setWebsite("http://google.com");
addPlace.setLanguage("French-IN");
Location location = new Location();
location.setLat("-38.383494");
location.setLng("33.427362");
addPlace.setLocation(location);
RestAssured.baseURI = "https://name.com";
String response = given().log().all().queryParam("key", "qaclick123")
.header("Content-Type", "application/json")
.body(addPlace).when().post("maps/api/place/add/json")
.then().log().all().assertThat().statusCode(200)
.body("scope", equalTo("APP"))
.header("Server", "Apache/2.4.41 (Ubuntu)")
.extract().response().asString();
Add place class
package day3.serialize.pojo;

import java.util.List;

public class AddPlace {
private String accuracy, name, phone_number, address, website, language;
private Location location;
private List<String> types;

public String getAccuracy() {
return accuracy;
}

public void setAccuracy(String accuracy) {
this.accuracy = accuracy;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPhone_number() {
return phone_number;
}

public void setPhone_number(String phone_number) {
this.phone_number = phone_number;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getWebsite() {
return website;
}

public void setWebsite(String website) {
this.website = website;
}

public String getLanguage() {
return language;
}

public void setLanguage(String language) {
this.language = language;
}

public Location getLocation() {
return location;
}

public void setLocation(Location location) {
this.location = location;
}

public List<String> getTypes() {
return types;
}

public void setTypes(List<String> types) {
this.types = types;
}
}
Location class
package day3.serialize.pojo;

public class Location {
private String lat, lng;

public String getLat() {
return lat;
}

public void setLat(String lat) {
this.lat = lat;
}

public String getLng() {
return lng;
}

public void setLng(String lng) {
this.lng = lng;
}
}
json
{
"location": {
"lat": -38.383494,
"lng": 33.427362
},
"accuracy": 50,
"name": "Frontline house",
"phone_number": "(+91) 983 893 3937",
"address": "29, side layout, cohen 09",
"types": [
"shoe park",
"shop"
],
"website": "http://google.com",
"language": "French-IN"
}

Spec builders

common code in request and response can be added in spec builders, so that it can be reused.
RequestSpecification requestSpecification = new RequestSpecBuilder().setBaseUri("https://name.com")
.addQueryParam("key", "qaclick123")
.setContentType(ContentType.JSON).build();

ResponseSpecification responseSpecification = new ResponseSpecBuilder().
expectStatusCode(200).
expectContentType(ContentType.JSON).
expectHeader("Server", "Apache/2.4.41 (Ubuntu)").build();

RequestSpecification request = given().log().all().spec(requestSpecification).body(addPlace);

String response = request.when().post("maps/api/place/add/json")
.then().log().all()
.assertThat().spec(responseSpecification)
.body("scope", equalTo("APP"))
.extract().response().asString();

JsonPath jsonPath = new JsonPath(response);
String place_id = jsonPath.getString("place_id");
System.out.println("Place id is "+place_id);

Logging

logging can be done to a file
addFilter(RequestLoggingFilter.logRequestTo(new PrintStream(new FileOutputStream("logging.txt"))))
.addFilter(ResponseLoggingFilter.logResponseTo(new PrintStream(new FileOutputStream("logging.txt"))))

run TestRunner via maven

  1. you can run TestRunner from maven with simple command mvn test by navigating to project folder
  2. you can pass arguments as well  mvn test -Dcucumber-options="--tags=@AddPlaceAPI"

maven cucumber reporting

Resources

Comments

Popular posts from this blog

Evaluate RAG - LLM's using RAGAS Python Pytest Framework

Spring Microservices

React Js and Redux