API Testing With Cucumber

An API represents a set of rules that enables services to communicate with each other. It is important to have a shared understanding of the API by the stakeholders. Behavior Driven Development (BDD) allows us to describe behaviors that are understandable to domain experts, testers, and developers through examples. A well-written example serves to represent a balanced view of competing concerns -

BDD is a very apt tool to test APIs. We will use Cucumber that supports BDD to test APIs developed in Java. One advantage of doing so is that the Java developer who implemented the API does not have to learn a new language to test it.

Test Automation Project

We have to set up a Java Maven Project that contains our BDD scenarios that we are going to automate. Include the following dependencies.

XML
 
    <dependencies>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-core</artifactId>
            <version>6.6.0</version>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>6.6.0</version>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>gherkin</artifactId>
            <version>15.0.2</version>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-jvm-deps</artifactId>
            <version>1.0.6</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>6.6.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-picocontainer</artifactId>
            <version>6.11.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-iostreams</artifactId>
            <version>2.14.1</version>
        </dependency>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20210307</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit-jupiter.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

Project Organization

The picture shows a sample project organization. It consists of the following folders under test.

Test automation

Gherkin Basics

Cucumber understands Gherkin, a structured natural language syntax based upon the Given/When/Then format. Gherkin allows us to write business readable API specifications that can also be used as automated tests. It is written in feature files which are plain text files with .feature extension.

Each feature file contains one or more scenarios. Each scenario is made up of one or more steps. Each step starts with one of five keywords: Given, When, Then, And, But. Given, When, Then introduce respectively the context, action, and outcome section of a scenario. And But are conjunctions that continue the preceding section. 

A sample test is shown below. A scenario should contain enough information to balance among its key features -  shared understanding, test, and documentation.

Gherkin
 
@Tag
Feature: Perform full demographic verification of a person

  Background:
    Given caller presents a valid access token

  Scenario: Perform full demographic verification with valid credentials
    Given a person with full demographic details
      | adhar_id            | 123456789        |
      | full_name           | John Doe         |
      | dob                 | 31/12/1990       |
      | phone_no            | 999999999999     |
      | email               | john@doe.com     |
    And the match threshold is set to 1.0
    When request is submitted for full demographic verification
    Then verify that the HTTP response is 200
    And a transaction id is returned

Each step in the feature file needs to be implemented in Java. In the step definition below, we

Java
 
public class Microservice1Steps {

    private final World world;
    private final Properties envConfig;
    private RequestSpecification request;

    public Microservice1Steps(World world) {
        this.world = world;
        this.envConfig = World.envConfig;
        this.world.featureContext = World.threadLocal.get();
    }

    @Before
    public void setUp() {
        request = RequestSpecificationFactory.getInstance(world);
    }

    @Given("caller presents a valid access token")
    public void getOAuth2Token() {
        String grantType = envConfig.getProperty("microservice1-grant_type");
        String clientId = envConfig.getProperty("microservice1-client_id");
        String clientSecret = envConfig.getProperty("microservice1-client_secret");
        String accessTokenUrl = envConfig.getProperty("microservice1-access_token_url");

        String body = String.format("grant_type=%s&client_secret=%s&client_id=%s", grantType, clientSecret, clientId);

        RequestSpecification request = RequestSpecificationFactory.getInstanceOAuth2();

        Response response = request
                .accept(ContentType.URLENC)
                .body(body)
                .post(accessTokenUrl);

        String responseString = response.then().extract().asString();
        String accessToken = new JSONObject(responseString).getString("access_token");

        world.scenarioContext.put("accessToken", accessToken);
    }
}

A set of scenarios may be run as a JUnit test:

Shell
 
mvn test -Denv=local -Dcucumber.filter.tags="@Tag"

Tag selects a set of tests to run and -Denv selects the environment to run the tests on.

The complete project is available on my GitHub.

Conclusion

In this article, we learned why BDD is a natural fit for testing API as it enhances shared understanding, tests, and documentation of the API. For Java developers, a Maven project with Cucumber libraries is easy to set up to add scenarios and test them.

 

 

 

 

Top