Functional Test Automation Using Rest API

Miguel Acosta Quinones
Hitachi Solutions Braintrust
4 min readJan 4, 2021

Test Automation is crucial to software development. Often teams struggle trying to move away from plain scripts, scenarios depending on each other, and even keeping data points in sync between the testing framework and the software being tested.

The keyword is Entities. In software development every real world representation of an object can be defined as an entity.
Examples: Clothing Items on an ecommerce website, automobiles on a dealership website or even pages in an informational website.

A rule of thumb for a scalable automation framework solution is that you need scenarios to be mutually exclusive (independent) of one and other.

Essentially the idea is to be able to access and edit data points in real time from the testing framework directly to the software under test.

For example, you don’t need to create a new clothing item every time you need to test your payment component. However, you need to get a price or even set a specific price in order to run your test scenarios. This is where Rest API comes to the rescue. All you need to do is make a Rest API request to the software being tested to find that clothing item and get its price. This removes the overhead of having to create a new clothing item EVERY SINGLE TIME you run this scenario.

For this example we will implement our testing framework using C#, Selenium, ChromeDriver & Specflow to test a web application with a Rest API server backend.

Here’s a simple framework setup which will spin up a ChromeDriver and instantiate a static Rest API class (see below) to be accessed throughout your framework:

using System;
using TechTalk.SpecFlow;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
public class Setup{ public static IWebDriver driver;
public static RestAPI restAPI;
[BeforeTestRun]
public static void BeforeTestRun()
{
this.driver = new ChromeDriver();
this.restAPI = new RestAPI(this.driver);
}
}

First, the Authorization. Most likely your application’s Rest API will not allow you to connect without some type of Authorization. For this example we will implement a Bearer authentication (also called Token authentication).

Assuming that you already have a script in place to create a session (also known as login) using Selenium and ChromeDriver. See below how we can retrieve that generated Token using the javascript executor of the WebDriver .

using System;
using OpenQA.Selenium;
public class RestAPI{
private IWebDriver driver;
public RestAPI(IWebDriver driver){
this.driver = driver;
}
private String getAuthToken(){
IJavaScriptExecutor jsExecutor = (IJavaScriptExecutor)driver;
String script = "return window.localStorage.getItem('Token')";
return (String)jsExecutor.ExecuteScript(script);
}
}

Note: the ‘Token’ location may vary depending on how the the Application’s session functionality was implemented.
Find out how to access your Application’s local storage by clicking here .

Now that we have a function to retrieve our current session’s Token we can proceed to making a RestAPI Request to our Application under test.

using System.Net.Http;
using System.Net.Http.Headers;
public class RestAPI{ public dynamic GET( String URL){
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", getAuthToken()); HttpResponseMessage response = client.GetAsync(processURL(URL)).Result; return ConsumeResponse(response); }}

Now we need to Consume Our Response:

using Newtonsoft.Json;public class RestAPI{   private dynamic ConsumeResponse(HttpResponseMessage response){      response.EnsureSuccessStatusCode();      String dataObjects = response.Content.ReadAsStringAsync().Result;      return JsonConvert.DeserializeObject<dynamic>(dataObjects);   }
}

With a RestAPI class in place, we need to implement a clean solution. Here’s how:

For this scenario we use the following Entity.

The RestAPI Response in Json of this GET Request is the following:

{
"id":1
"Price": 499,
"Title": "ITEM TITLE",
"Description": "here is where we need some sort of description about this item, scentially no more than 3-4 lines in length. Please be mindfull that we can modify this section's theme as you wish.",
"Details": "Some more details"
}

For our framework we will create the following class:

using System;public class Entity{
public readonly int Id;
public Policy(int Id){
this.Id = Id;
}
public dynamic GetProperties(){
return Setup.restAPI.GET($"/entity/{Id}");
}
public string Price{ get{ return this.GetProperties()["Price"] }}}

Finally, let’s actually use our Entity class to get it’s price.

Feature:

Scenario: Checkout Pricing is correctGiven User landed in Entity 1
When User clicks checkout
Then Checkout page should display correctly
And Taxes should be 7.5%

Step Definition:

public class Checkout{   public Entity entity;   [Given(@"User landed in Entity (.*)")]   public void GivenUserLandedInEntity(int p0){   entity = new Entity(p0)   }   [When(@"User clicks checkout")]
public void WhenUserClicksCheckout()
{//implement action of clicking checkout button}
[Then(@"Checkout page should display correctly")]
public void ThenCheckoutPageShouldDisplayCorrectly()
{//implement testing of the checkout page}
[Then(@"Taxes should be (.*)%")]
public void ThenTaxesShouldBe(float p0){
float expectedTax= float.Parse(this.entity.Price) * p0;
//implement test to make sure expectedTax to be in the
//checkout page
}}

Summary

The intent of this article was to introduce you to the concept of using Rest API in your automation framework. The above approach is best used for functional, load or performance Test Automation. This solution can be substantially scalable to edit/add entities in the software under test while keeping consistent data across both, the automation framework and the application under test.

This concept is not limited only to Rest API. One could also use query execution directly into the database under test in order to implement the same model.

--

--