Published on

API Testing with Cucumber BDD

Authors

API Testing with Cucumber BDD

API testing is crucial for ensuring that the backend of an application works as expected, reliably, and efficiently. Behavior Driven Development (BDD) with Cucumber allows teams to create tests in a human-readable format, promoting collaboration and clarity. In this post, we'll explore how to set up API testing using Cucumber, write BDD scenarios, and implement step definitions for testing APIs.

Table of Contents

1. Introduction to API Testing with Cucumber BDD

API testing focuses on validating the business logic, data responses, and performance of server-side endpoints. Cucumber, a BDD tool, allows tests to be written in Gherkin, a plain-text language that makes tests understandable for non-technical stakeholders. Using Cucumber for API testing encourages collaboration between developers, testers, and business analysts, ensuring everyone is on the same page.

2. Setting Up a Project for API Testing with Cucumber

Step-by-Step Setup:

  1. Install Required Tools:

    • Install Java Development Kit (JDK) and Maven (for Java projects).
    • Install an IDE like IntelliJ IDEA or Eclipse.
    • Install Cucumber and REST Assured dependencies for API testing.
  2. Configure Maven Project:

    • Create a new Maven project and add the following dependencies to your pom.xml file:
<dependencies>
    <!-- Cucumber Dependencies -->
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <version>7.14.0</version>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-junit</artifactId>
        <version>7.14.0</version>
    </dependency>
    <!-- REST Assured Dependency for API Testing -->
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>5.3.0</version>
    </dependency>
</dependencies>
  1. Create the Project Structure:
    • Organize the project with src/test/java for step definitions and src/test/resources for feature files.

3. Writing Gherkin Scenarios for API Testing

Create a feature file named user_api.feature in the src/test/resources/features directory.

Feature: User API

  Scenario: Create a new user successfully
    Given the API endpoint is set to "/users"
    When a POST request is sent with valid user data
    Then the response status code should be 201
    And the response body should contain the user's information

  Scenario: Fetch user details successfully
    Given the API endpoint is set to "/users/{id}"
    When a GET request is sent for user with ID "1"
    Then the response status code should be 200
    And the response body should contain the user's information

  Scenario: User not found when fetching details
    Given the API endpoint is set to "/users/{id}"
    When a GET request is sent for user with ID "999"
    Then the response status code should be 404
    And the response body should contain "User not found"

4. Implementing Step Definitions for API Testing

Step definitions map Gherkin steps to Java methods using REST Assured for HTTP requests. Create a Java class named UserApiSteps.java under src/test/java/step_definitions.

package step_definitions;

import io.cucumber.java.en.*;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import org.junit.Assert;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;

public class UserApiSteps {

    private static final String BASE_URL = "https://example.com/api";
    private RequestSpecification request;
    private Response response;
    private String endpoint;

    @Given("the API endpoint is set to {string}")
    public void the_api_endpoint_is_set_to(String endpoint) {
        this.endpoint = BASE_URL + endpoint;
        RestAssured.baseURI = BASE_URL;
        request = given().header("Content-Type", "application/json");
    }

    @When("a POST request is sent with valid user data")
    public void a_post_request_is_sent_with_valid_user_data() {
        String requestBody = "{ \"name\": \"John Doe\", \"email\": \"[email protected]\" }";
        response = request.body(requestBody).post(endpoint);
    }

    @When("a GET request is sent for user with ID {string}")
    public void a_get_request_is_sent_for_user_with_id(String userId) {
        response = request.get(endpoint.replace("{id}", userId));
    }

    @Then("the response status code should be {int}")
    public void the_response_status_code_should_be(int statusCode) {
        response.then().statusCode(statusCode);
    }

    @Then("the response body should contain the user's information")
    public void the_response_body_should_contain_user_information() {
        response.then().body("name", notNullValue()).body("email", notNullValue());
    }

    @Then("the response body should contain {string}")
    public void the_response_body_should_contain(String message) {
        Assert.assertTrue(response.getBody().asString().contains(message));
    }
}

5. Running Cucumber Tests

To run the Cucumber tests, use a test runner class. Create a Java class named RunCucumberTest.java under src/test/java/runner.

package runner;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(
        features = "src/test/resources/features",
        glue = "step_definitions",
        plugin = {"pretty", "html:target/cucumber-reports"}
)
public class RunCucumberTest {
}

Run the RunCucumberTest class to execute the scenarios. The results will be displayed in the console, and an HTML report will be generated in the target/cucumber-reports folder.

6. Advanced Cucumber Features for API Testing

Using Hooks:

Hooks allow you to perform actions before and after each scenario, such as setting up or tearing down a test environment.

import io.cucumber.java.After;
import io.cucumber.java.Before;

public class Hooks {

    @Before
    public void setUp() {
        // Set up code, like initializing RestAssured configurations
        RestAssured.baseURI = "https://example.com/api";
    }

    @After
    public void tearDown() {
        // Code to clean up or reset the state
    }
}

7. Best Practices for API Testing with Cucumber BDD

  • Organize Feature Files and Steps: Keep related API scenarios together and use descriptive names.
  • Use Data Tables for Input Variations: Utilize Cucumber's Data Tables to handle different test data combinations without repeating steps.
  • Ensure Idempotency: Make sure API tests are idempotent and do not affect each other, using setup and teardown methods wisely.

8. Integrating Cucumber with CI/CD Pipelines

To integrate Cucumber with CI/CD pipelines like Jenkins or GitHub Actions, configure the pipeline to run mvn test and archive the test reports generated in the target/cucumber-reports directory.

9. Conclusion

API testing with Cucumber BDD provides a readable, maintainable approach to validating the business logic and performance of backend services. By writing scenarios in plain English, you can ensure everyone on the team understands and contributes to the testing process.

10. Further Reading and Resources

By following this guide, you should be able to set up API testing with Cucumber, write effective Gherkin scenarios, implement robust step definitions, and run tests seamlessly.