Building a Test Automation Framework using Cypress.io — Adding API Testing (Part 3)

Building a Test Automation Framework using Cypress.io — Adding API Testing (Part 3)

Hola!

In Part-2 of this article series we added Cucumber BDD support to our Test Automation Framework. In this article we will build on top of this, and add native API testing support. Native? using only cypress and no external JS libraries. Promesa.

API testing with Cypress

What is an API? and why test it?

API testing is a type of software testing that involves testing application programming interfaces (APIs) directly and as part of integration testing to determine if they meet expectations for functionality, reliability, performance, and security. Since APIs lack a GUI, API testing is performed at the message layer.

API testing is now considered critical for automating testing because APIs now serve as the primary interface to application logic and because GUI tests are difficult to maintain with the short release cycles and frequent changes commonly used with Agile software development and DevOps.[1]

API tests are fast, give high ROI, and simplify the validation of business logic, security, compliance, and other aspects of the application. In cases where the API is a public one, providing end-users programmatic access to our application or services, API tests effectively become end-to-end tests and should cover a complete user story.[2]

Now that we know the what and the why, Let's Begin!!

Writing an API Test case

As part of this article, we are gonna be using a free API endpoint from PokéAPI.
PokéAPI provides lots of free Pokémon data using simple REST APIs.
We are gonna be using following Endpoint:

https://pokeapi.co/api/v2/pokemon/{pokemon_name or id}

This endpoint provides data for a Pokémon by providing a name or index.

Using API resource on https://pokeapi.co

Let's create an API directory under /feature folder, to store our API tests.
And add a feature file name Pokemon_test.feature

Creating BDD test for Pokemon API

Now add following steps for the BDD API test:

 @API
 Feature: Pokemon GET /pokemon/{pokemon_name or id}
    @smoke @test
    Scenario: Fetch data for a pokemon using API and verify it
        Given As a user I want to execute Pokemon GET api for Pokemon "pikachu"
        Then Verify response status code is 200
        And Verify response details for Pokemon "pikachu"
API positive test case

We are going to make an API request for Pokémon endpoint and then validate request response code, response data and cookies present in response.

Writing GLUE Code for API Test:

So far we had a single Glue Code file for holding our UI test step-definitions. Now before we add step definitions for API steps, it makes it to segregate things a bit.

We are gonna be adding API & UI folders under stepDefinitions directory and move commonSteps.js to UI folder as it contains UI test steps only. Now under API folder, add new step-definition file apiSteps.js like shown below:

Re-organized project structure

Now we will add step-definition code in apiSteps.js for Given step

...
 Given As a user I want to execute Pokemon GET api for Pokemon "pikachu"
 ...

Corresponding Glue code, which includes initializing Cy.request() :

Cy.request() is the utility which is used for making HTTP request in Cypress.

Given('As a user I want to execute Pokemon GET api for Pokemon {string}', (pokename) => {
    cy.request({
      method: 'GET',
      url: 'https://pokeapi.co/api/v2/pokemon/' + pokename,
      headers: {
        'Content-Type': 'application/json'  
      },
      failOnStatusCode:false
    }).as('get_pokemon_data')
    
    //here 'get_pokemon_data'is alias for this request to be used by other steps
  
  });
API positive test case
here, we are providing request argument as failOnStatusCode:false
which is true by default, so run steps even when request status code is other than 2XX or 3XX.
More arguments can be found at Cypress Docs.

Similarly we can write glue code for other steps:

Here we are validating request status code and in subsequent step response body:

...
 Then('Verify response status code is {int}', (statusCode) => {
    cy.get('@get_pokemon_data').should((response)=> {
      expect(response.status).to.eq(statusCode);
      
    })
  });

  Then('Verify response details for Pokemon {string}', (pokename) => {
    cy.get('@get_pokemon_data').should((response)=> {
      
      expect(response.body).to.have.property('abilities');
      //Different ways of validating nested properties
      //1st Way
      expect(response.body).to.have.nested.property('forms[0].name',pokename);
      //2nd Way
      expect(response.body.forms[0]).to.have.property('name',pokename);
      //3rd Way
      const name = response.body.forms[0].name;
      assert.equal(name, pokename);
      
      expect(response).to.have.property('headers');
    })
  });
  

Now in order to run this, we can provide API test specific tags in --env TAGS, so that only API test is run:

npx cypress run --env TAGS="@API"
API Test Run result
Here we can see only API test feature file was passing and the UI feature test file Test1.feature is in pending (meaning it wasn't run)

Adding negative API test case

So far we had been running a test where we give a valid Pokémon name in query param, now we will write a test case where we will provide invalid Pokémon name so that we will get a HTTP status code 404.

We are gonna be using same Endpoint:

https://pokeapi.co/api/v2/pokemon/{invalid_pokemon_name}


In our test case, we are gonna provide a Pokémon name which is invalid such as "doraemon"

@smoke @test @negative
    Scenario: Fetch data for an invalid pokemon using API and verify it
        Given As a user I want to execute Pokemon GET api for Pokemon "doraemon"
        Then Verify response status code is 404
Negative API test case

Now in order to run both test cases we will once again run the following command:

npx cypress run --env TAGS="@API"
Test Run Result

So in this article, we added 2 API test cases and validated them for Response code and response body (for positive test case) using only inbuilt Cypress utilities.

See you next time, when we will be adding some advance things to our Frameworks such as page object model, environment support and reports etc. So keep tuned in.

Adiós!

GitHub repo: https://github.com/far11ven/Cypress-TestFramework/tree/develop/Part 03