REST API Testing Framework
We will be creating a simple Rest Testing Framework in Java and JUnit that could be used for any testing scenarios.
Rest Testing Framework Overview
The framework should be able to execute the basic REST operations (GET, POST, PUT, PATCH, DELETE) and perform the validations on the code, message, headers and body of the response.
The completed code can be accessed from my GitHub account from where you can collect and make modifications based on your requirements.
Design
We will be having three classes for the framework (in packagecom.axatrikx.controller)
RestExecutor : Performs the HTTP operations using Apache Http Client
RestResponse : A javabean class to hold our response values (code, message, headers, body)
RestValidator : Validates the response with the expected values
RestResponse : A javabean class to hold our response values (code, message, headers, body)
RestValidator : Validates the response with the expected values
The package com.axatrikx.test holds the test scripts.
Note: I will be using ‘json-server‘ to fake the REST API. Its a real handy tool to rapidly setup fake REST API for testing your framework.
We are planning to have the framework implementation in the way shown below.
RestExecutor executor = new RestExecutor(“http://localhost:3000”);
// Get operation with validations
executor.get(“/posts/1”)
.expectCode(200)
.expectMessage(“OK”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“userId”);
// Get operation with validations
executor.get(“/posts/1”)
.expectCode(200)
.expectMessage(“OK”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“userId”);
RestResponse
Its a JavaBean class holding the values from the response for each request. Four variables are provided each having its own getters and setters. See the class here.
import java.util.HashMap;
public class RestResponse {
private int responseCode;
private String responseBody;
private HashMap<String, String> headers;
private String responseMessage;public RestResponse() {
headers = new HashMap<String, String>();
}public int getResponseCode() {
return responseCode;
}
public class RestResponse {
private int responseCode;
private String responseBody;
private HashMap<String, String> headers;
private String responseMessage;public RestResponse() {
headers = new HashMap<String, String>();
}public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
this.responseCode = responseCode;
}
public String getResponseBody() {
return responseBody;
}
return responseBody;
}
public void setResponseBody(String responseBody) {
this.responseBody = responseBody;
}
this.responseBody = responseBody;
}
public HashMap<String, String> getHeaders() {
return headers;
}
return headers;
}
public String getHeader(String name) {
return headers.get(name);
}
return headers.get(name);
}
public void setHeader(String name, String value) {
this.headers.put(name, value);
}
this.headers.put(name, value);
}
public String getResponseMessage() {
return responseMessage;
}
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
this.responseMessage = responseMessage;
}
}
RestValidator
This class performs the validation of the response with the expected values. It takes a RestResponse object in its constructor.
It has a method for each type of validations performed and all the methods returns the same object reference which enables chaining possible. The assertions are done using Junit assertions. See the class here.
The code is self explanatory and I’ve added a printBody() method to print the response body during script creation or debugging. This method also returns the Validator object and therefore can be used in the chain at any point.
It has a method for each type of validations performed and all the methods returns the same object reference which enables chaining possible. The assertions are done using Junit assertions. See the class here.
The code is self explanatory and I’ve added a printBody() method to print the response body during script creation or debugging. This method also returns the Validator object and therefore can be used in the chain at any point.
import java.util.HashMap;
import java.util.Set;
import org.junit.Assert;public class RestValidator {private RestResponse response;
import java.util.Set;
import org.junit.Assert;public class RestValidator {private RestResponse response;
RestValidator(RestResponse response) {
this.response = response;
}
this.response = response;
}
public RestValidator expectCode(int expectedCode) {
Assert.assertEquals(“Incorrect Response Code”, expectedCode, response.getResponseCode());
return this;
}
Assert.assertEquals(“Incorrect Response Code”, expectedCode, response.getResponseCode());
return this;
}
public RestValidator expectMessage(String message) {
Assert.assertEquals(“Incorrect Response Message”, message, response.getResponseMessage());
return this;
}
Assert.assertEquals(“Incorrect Response Message”, message, response.getResponseMessage());
return this;
}
public RestValidator expectHeader(String headerName, String headerValue) {
Assert.assertEquals(“Incorrect header – ” + headerName, headerValue, response.getHeader(headerName));
return this;
}
Assert.assertEquals(“Incorrect header – ” + headerName, headerValue, response.getHeader(headerName));
return this;
}
public RestValidator expectHeaders(HashMap<String, String> headers) {
Set<String> keys = headers.keySet();
for (String key : keys) {
Assert.assertEquals(“Incorrect header – ” + key, headers.get(key), response.getHeader(key));
}
return this;
}
Set<String> keys = headers.keySet();
for (String key : keys) {
Assert.assertEquals(“Incorrect header – ” + key, headers.get(key), response.getHeader(key));
}
return this;
}
public RestValidator expectInBody(String content) {
Assert.assertTrue(“Body doesnt contain string : ” + content,
response.getResponseBody().contains(content));
return this;
}
Assert.assertTrue(“Body doesnt contain string : ” + content,
response.getResponseBody().contains(content));
return this;
}
public RestValidator printBody(){
System.out.println(response.getResponseBody());
return this;
}
System.out.println(response.getResponseBody());
return this;
}
public RestResponse getResponse(){
return response;
}
return response;
}
}
RestExecutor
This class performs the HTTP operations using Apache HttpClient library and would be having a method for each of the operations. We will look into two methods in detail an you can check the code for the rest.
Constructor
The constructor is a simple one which takes in the url and initilizes the HttpClient object.
The constructor is a simple one which takes in the url and initilizes the HttpClient object.
private HttpClient client;
private String url;public RestExecutor(String url) {
client = HttpClientBuilder.create().build();
this.url = url;
}
private String url;public RestExecutor(String url) {
client = HttpClientBuilder.create().build();
this.url = url;
}
GET Method
The GET method would have the path as one parameter and a HashMap as the second parameter for the headers provided as key value pairs. The method would be returning a RestValidator object containing the values corresponding to the response values of the GET request.
public RestValidator get(String path, HashMap<String, String> headers) {
HttpGet request = new HttpGet(url + path);
HttpResponse response;
/*
* The response object which holds the details of the response.
*/
RestResponse resResponse = new RestResponse();
StringBuffer responseString = new StringBuffer();
try {
/*
* Setting the headers for the request
*/
if (headers != null) {
Set<String> keys = headers.keySet();
for (String key : keys) {
request.addHeader(key, headers.get(key));
}
}
/*
* Executing the GET operation
*/
response = client.execute(request);/*
* Obtaining the response body from the response stream.
*/
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = “”;
while ((line = rd.readLine()) != null) {
responseString.append(line);
}
/*
* Setting values for the response object
*/
resResponse.setResponseBody(responseString.toString());
resResponse.setResponseCode(response.getStatusLine().getStatusCode());
resResponse.setResponseMessage(response.getStatusLine().getReasonPhrase());
Header[] rheaders = response.getAllHeaders();
for (Header header : rheaders) {
resResponse.setHeader(header.getName(), header.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
/*
* Returns the RestValidator object providing the response object
*/
return new RestValidator(resResponse);
}
HttpGet request = new HttpGet(url + path);
HttpResponse response;
/*
* The response object which holds the details of the response.
*/
RestResponse resResponse = new RestResponse();
StringBuffer responseString = new StringBuffer();
try {
/*
* Setting the headers for the request
*/
if (headers != null) {
Set<String> keys = headers.keySet();
for (String key : keys) {
request.addHeader(key, headers.get(key));
}
}
/*
* Executing the GET operation
*/
response = client.execute(request);/*
* Obtaining the response body from the response stream.
*/
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = “”;
while ((line = rd.readLine()) != null) {
responseString.append(line);
}
/*
* Setting values for the response object
*/
resResponse.setResponseBody(responseString.toString());
resResponse.setResponseCode(response.getStatusLine().getStatusCode());
resResponse.setResponseMessage(response.getStatusLine().getReasonPhrase());
Header[] rheaders = response.getAllHeaders();
for (Header header : rheaders) {
resResponse.setHeader(header.getName(), header.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
/*
* Returns the RestValidator object providing the response object
*/
return new RestValidator(resResponse);
}
The HttpGet method is initialized with the combined URL, headers are set and request is executed to obtain the response. The response is then processed into our RestResponse object taking in our required values. This response object is used to initialize a RestValidator object corresponding to this GET request which is returned to the user to perform validations.
Note that I have added another GET method without the header parameter. This method just calls the above method with null value for header
public RestValidator get(String path) {
return get(path, null);
}
return get(path, null);
}
POST Method
Post Method is similar to the GET method above, the only difference being the presence of xml body.
Post Method is similar to the GET method above, the only difference being the presence of xml body.
public RestValidator post(String path, HashMap<String, String> headers, String xmlContent, String contentType) {
HttpPost post = new HttpPost(url + path);
RestResponse resResponse = new RestResponse();
StringBuffer responseString = new StringBuffer();
try {
if (headers != null)
post.setEntity(getEntities(headers));/*
* Setting the xml content and content type.
*/
StringEntity input = new StringEntity(xmlContent);
input.setContentType(contentType);
post.setEntity(input);
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = “”;
while ((line = rd.readLine()) != null) {
responseString.append(line);
}
resResponse.setResponseBody(responseString.toString());
resResponse.setResponseCode(response.getStatusLine().getStatusCode());
resResponse.setResponseMessage(response.getStatusLine().getReasonPhrase());
Header[] rheaders = response.getAllHeaders();
for (Header header : rheaders) {
resResponse.setHeader(header.getName(), header.getValue());
}
} catch (Exception e) {
e.printStackTrace(); // handle
}
return new RestValidator(resResponse);
}
HttpPost post = new HttpPost(url + path);
RestResponse resResponse = new RestResponse();
StringBuffer responseString = new StringBuffer();
try {
if (headers != null)
post.setEntity(getEntities(headers));/*
* Setting the xml content and content type.
*/
StringEntity input = new StringEntity(xmlContent);
input.setContentType(contentType);
post.setEntity(input);
HttpResponse response = client.execute(post);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = “”;
while ((line = rd.readLine()) != null) {
responseString.append(line);
}
resResponse.setResponseBody(responseString.toString());
resResponse.setResponseCode(response.getStatusLine().getStatusCode());
resResponse.setResponseMessage(response.getStatusLine().getReasonPhrase());
Header[] rheaders = response.getAllHeaders();
for (Header header : rheaders) {
resResponse.setHeader(header.getName(), header.getValue());
}
} catch (Exception e) {
e.printStackTrace(); // handle
}
return new RestValidator(resResponse);
}
A simpler POST method without headers is also provided.
For the remaining methods, check out the complete class.
Now that the core of our framework is done, lets see how to write the test scripts.
TestScript
Since we are using jUnit as the unit testing framework, we can initialize the RestExecutor object in the @BeforeClass method and use the object in the @Test methods.
import org.junit.BeforeClass;
import org.junit.Test;
import com.axatrikx.controller.RestExecutor;public class TestScript {
private static final String URL = “http://localhost:3000”;
private static RestExecutor executor;@BeforeClass
public static void setUp() {
/*
* Initialize RestExecutor object using the end point URL
*/
executor = new RestExecutor(URL);
}
import org.junit.Test;
import com.axatrikx.controller.RestExecutor;public class TestScript {
private static final String URL = “http://localhost:3000”;
private static RestExecutor executor;@BeforeClass
public static void setUp() {
/*
* Initialize RestExecutor object using the end point URL
*/
executor = new RestExecutor(URL);
}
@Test
public void testGETMethod() {
/*
* Performs GET operation on http://localhost:3000/posts.
* Note that we give only the path in the get method as we use
* the domain part while initializing the RestExecutor object
*/
executor.get(“/posts”)
.expectCode(200) // Expected code of 200
.expectMessage(“OK”) // Expected Message of ‘OK’
.expectHeader(“Content-Type”, “application/json; charset=utf-8”) // Content-Type header value
.expectInBody(“rest testing framework”) // Content inside the response body
.expectInBody(“webdriver framework”) // Another Content inside the response body
.expectInBody(“axatrikx”); // Yet Another Content inside the response body
public void testGETMethod() {
/*
* Performs GET operation on http://localhost:3000/posts.
* Note that we give only the path in the get method as we use
* the domain part while initializing the RestExecutor object
*/
executor.get(“/posts”)
.expectCode(200) // Expected code of 200
.expectMessage(“OK”) // Expected Message of ‘OK’
.expectHeader(“Content-Type”, “application/json; charset=utf-8”) // Content-Type header value
.expectInBody(“rest testing framework”) // Content inside the response body
.expectInBody(“webdriver framework”) // Another Content inside the response body
.expectInBody(“axatrikx”); // Yet Another Content inside the response body
/*
* GET for a specific item
*/
executor.get(“/posts/1”)
.expectCode(200)
.expectMessage(“OK”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“rest testing framework”)
.expectInBody(“axatrikx”);
* GET for a specific item
*/
executor.get(“/posts/1”)
.expectCode(200)
.expectMessage(“OK”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“rest testing framework”)
.expectInBody(“axatrikx”);
/*
* GET for a seach query
*/
executor.get(“/posts?title=rest%20testing%20framework&author=axatrikx”)
.expectCode(200)
.expectMessage(“OK”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“rest testing framework”)
.expectInBody(“axatrikx”);
}
* GET for a seach query
*/
executor.get(“/posts?title=rest%20testing%20framework&author=axatrikx”)
.expectCode(200)
.expectMessage(“OK”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“rest testing framework”)
.expectInBody(“axatrikx”);
}
@Test
public void testPOSTMethod() {
/*
* POST operation for insertion providing the path, xml content and content type.
*/
executor.post(“/posts”, “{ \”title\”: \”new test\”, \”author\”: \”axatrikx\” }”, “application/json”)
.expectCode(201)
.expectMessage(“Created”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“\”title\”: \”new test\””)
.expectInBody(“axatrikx”);
}
}
public void testPOSTMethod() {
/*
* POST operation for insertion providing the path, xml content and content type.
*/
executor.post(“/posts”, “{ \”title\”: \”new test\”, \”author\”: \”axatrikx\” }”, “application/json”)
.expectCode(201)
.expectMessage(“Created”)
.expectHeader(“Content-Type”, “application/json; charset=utf-8”)
.expectInBody(“\”title\”: \”new test\””)
.expectInBody(“axatrikx”);
}
}
The executor.get(“/posts”) returns the RestValidator object having the RestResponse corresponding to the request. We can use the various validation methods on the object to perform the validation. As you might have noticed in RestValidator class, the assertions are using junit with the error messages preset. The remaining validations will be skipped if any one of the validations fail. Again, this can be modified according to your needs.
The same method is followed in the post request for the second @Test method. I haven’t added a proper reporting mechanism for this framework, but you can use any reporting libraries for JUnit or create one on your own.
See the completed project in github.
I’d suggest to make this framework as Generic so that this can be used project independent. You can refer my article Most commonly used methods
I’d suggest to make this framework as Generic so that this can be used project independent. You can refer my article Most commonly used methods
-----------------------------------------------------------------------------------------------------------------------
What is API :
An application-programming interface (API) is a set of programming instructions and standards for accessing a Web-based software application or Web tool.
Example :
An API is a software-to-software interface, not a user interface. With APIs, applications talk to each other without any user knowledge or intervention. When you buy movie tickets online and enter your credit card information, the movie ticket Web site uses an API to send your credit card information to a remote application that verifies whether your information is correct. Once payment is confirmed, the remote application sends a response back to the movie ticket Web site saying it’s OK to issue the tickets.
What is JSON :
JSON (JavaScript Object Notation) is a lightweight, text-based, language-independent data exchange format that is easy for humans and machines to read and write. JSON can represent two structured types: objects and arrays. An object is an unordered collection of zero or more name/value pairs. An array is an ordered sequence of zero or more values. The values can be strings, numbers, booleans, null, and these two structured types.
Example : You can see it here.
JSON is an easier-to-use alternative to XML.
Note : Here i am just conferring how to parse and How to convert JsonObject to JavaObject.So that you could use it your live projects using selenium.
Application contentType : Json
Application contentType : Json
Objective : To validate Address : Chicago, IL, USA.
GoogleMaps API : http://maps.googleapis.com/maps/api/geocode/json?address=chicago&sensor=false
GoogleMaps API : http://maps.googleapis.com/maps/api/geocode/json?address=chicago&sensor=false
We can easily parse Json using “json-lib-2.4-jdk15.jar” Download from here…!
Please consider the below code as a reference.
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import org.json.JSONArray;
import org.json.JSONObject;
import org.testng.Reporter;
import org.testng.annotations.Test;
import org.json.JSONObject;
import org.testng.Reporter;
import org.testng.annotations.Test;
public class ReadJsonObject{
@Test
public void aptTesting() throws Exception {
try {
URL url = new URL(
“http://maps.googleapis.com/maps/api/geocode/json?address=chicago&sensor=false”);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(“GET”);
conn.setRequestProperty(“Accept”, “application/json”);
@Test
public void aptTesting() throws Exception {
try {
URL url = new URL(
“http://maps.googleapis.com/maps/api/geocode/json?address=chicago&sensor=false”);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(“GET”);
conn.setRequestProperty(“Accept”, “application/json”);
if (conn.getResponseCode() != 200) {
throw new RuntimeException(” HTTP error code : ”
+ conn.getResponseCode());
}
throw new RuntimeException(” HTTP error code : ”
+ conn.getResponseCode());
}
Scanner scan = new Scanner(url.openStream());
String entireResponse = new String();
while (scan.hasNext())
entireResponse += scan.nextLine();
String entireResponse = new String();
while (scan.hasNext())
entireResponse += scan.nextLine();
System.out.println(“Response : “+entireResponse);
scan.close();
JSONObject obj = new JSONObject(entireResponse );
String responseCode = obj.getString(“status”);
System.out.println(“status : ” + responseCode);
String responseCode = obj.getString(“status”);
System.out.println(“status : ” + responseCode);
JSONArray arr = obj.getJSONArray(“results”);
for (int i = 0; i < arr.length(); i++) {
String placeid = arr.getJSONObject(i).getString(“place_id”);
System.out.println(“Place id : ” + placeid);
String formatAddress = arr.getJSONObject(i).getString(
“formatted_address”);
System.out.println(“Address : ” + formatAddress);
for (int i = 0; i < arr.length(); i++) {
String placeid = arr.getJSONObject(i).getString(“place_id”);
System.out.println(“Place id : ” + placeid);
String formatAddress = arr.getJSONObject(i).getString(
“formatted_address”);
System.out.println(“Address : ” + formatAddress);
//validating Address as per the requirement
if(formatAddress.equalsIgnoreCase(“Chicago, IL, USA”))
{
System.out.println(“Address is as Expected”);
}
else
{
System.out.println(“Address is not as Expected”);
}
}
if(formatAddress.equalsIgnoreCase(“Chicago, IL, USA”))
{
System.out.println(“Address is as Expected”);
}
else
{
System.out.println(“Address is not as Expected”);
}
}
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
That’s it, Now you know to convert JsonObject to Java Object and use it in your Selenium snippet.
Google Example :
Google Example :
package automationFramework;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net. MalformedURLException;
import java.net.URL;
import java.util.Scanner;
import org.json.JSONArray;
import org.json.JSONObject;
import org.testng.Reporter;
import org.testng.annotations.Test;
@SuppressWarnings("unused")
public class RestAPI{
@Test
public void aptTesting() throws Exception {
try {
URL url = new URL("http://maps.googleapis. com/maps/api/geocode/json? address=chicago&sensor=false&# 8221");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty(" Accept”, “application/json", null);
if (conn.getResponseCode() != 200) {
throw new RuntimeException("HTTP error code:" + conn.getResponseCode());
}
Scanner scan = new Scanner(url.openStream());
String entireResponse = new String();
while (scan.hasNext())
entireResponse += scan.nextLine();
System.out.println("Response :" + entireResponse);
scan.close();
JSONObject obj = new JSONObject(entireResponse );
String responseCode = obj.getString("status");
System.out.println("status :" + responseCode);
JSONArray arr = obj.getJSONArray("results");
for (int i = 0; i < arr.length(); i++) {
String placeid = arr.getJSONObject(i). getString("place_id");
System.out.println("Place id :" + placeid);
String formatAddress = arr.getJSONObject(i). getString("formatted_address") ;
System.out.println("Address :" + formatAddress);
//validating Address as per the requirement
if(formatAddress. equalsIgnoreCase("Chicago, IL, USA"))
{
System.out.println("Address is as Expected");
}
else
{
System.out.println("Address is not as Expected");
}
}
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Comments
Post a Comment