Advanced Chapter 17 · 11 min read

API Testing with Cucumber

Write BDD scenarios for REST API testing — combine Gherkin with HTTP clients for API validation without a browser.

API Testing with Cucumber

Cucumber isn't just for UI testing. BDD scenarios work beautifully for API testing — expressing expected API behavior in business-readable Gherkin while executing HTTP requests under the hood.

api.feature
Feature: User API
  @api
  Scenario: Get all users
    Given the API base URL is "https://jsonplaceholder.typicode.com"
    When I send a GET request to "/users"
    Then the response status code should be 200
    And the response should contain 10 users

  @api
  Scenario: Create a new user
    Given the API base URL is "https://jsonplaceholder.typicode.com"
    When I send a POST request to "/posts" with:
      | title  | BDD API Testing          |
      | body   | Testing APIs with Gherkin |
      | userId | 1                        |
    Then the response status code should be 201
    And the response should contain "id"

  @api
  Scenario: Get user by ID
    Given the API base URL is "https://jsonplaceholder.typicode.com"
    When I send a GET request to "/users/1"
    Then the response status code should be 200
    And the response field "name" should be "Leanne Graham" 
ApiSteps.java
package com.bdd.course.stepdefinitions;

import io.cucumber.java.en.*;
import io.cucumber.datatable.DataTable;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import static org.hamcrest.Matchers.*;
import java.util.Map;

public class ApiSteps {

    private String baseUrl;
    private Response response;

    @Given("the API base URL is {string}")
    public void setBaseUrl(String url) {
        this.baseUrl = url;
    }

    @When("I send a GET request to {string}")
    public void sendGet(String endpoint) {
        response = RestAssured.given()
            .baseUri(baseUrl)
            .when().get(endpoint);
    }

    @When("I send a POST request to {string} with:")
    public void sendPost(String endpoint, DataTable table) {
        Map<String, String> body = table.asMap(String.class, String.class);
        response = RestAssured.given()
            .baseUri(baseUrl)
            .contentType("application/json")
            .body(body)
            .when().post(endpoint);
    }

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

    @Then("the response should contain {int} users")
    public void verifyUserCount(int count) {
        response.then().body("size()", equalTo(count));
    }

    @Then("the response should contain {string}")
    public void responseContainsField(String field) {
        response.then().body(field, notNullValue());
    }

    @Then("the response field {string} should be {string}")
    public void responseFieldEquals(String field, String value) {
        response.then().body(field, equalTo(value));
    }
}
api.feature
Feature: User API
  @api
  Scenario: Get all users
    Given the API base URL is "https://jsonplaceholder.typicode.com"
    When I send a GET request to "/users"
    Then the response status code should be 200
    And the response should contain 10 users

  @api
  Scenario: Create a new user
    Given the API base URL is "https://jsonplaceholder.typicode.com"
    When I send a POST request to "/posts" with:
      | title  | BDD API Testing          |
      | body   | Testing APIs with Gherkin |
      | userId | 1                        |
    Then the response status code should be 201
api_steps.py
# features/steps/api_steps.py
import requests
from behave import given, when, then

@given('the API base URL is "{url}"')
def step_base_url(context, url):
    context.base_url = url

@when('I send a GET request to "{endpoint}"')
def step_get(context, endpoint):
    context.response = requests.get(f'{context.base_url}{endpoint}')

@when('I send a POST request to "{endpoint}" with')
def step_post(context, endpoint):
    body = {row['key']: row['value'] for row in context.table}
    # Alternative: if table has named columns
    if hasattr(context.table, 'headings') and len(context.table.headings) == 2:
        body = {}
        for row in context.table:
            body[row[0]] = row[1]
    context.response = requests.post(
        f'{context.base_url}{endpoint}', json=body)

@then('the response status code should be {code:d}')
def step_status(context, code):
    assert context.response.status_code == code

@then('the response should contain {count:d} users')
def step_user_count(context, count):
    assert len(context.response.json()) == count

@then('the response field "{field}" should be "{value}"')
def step_field_equals(context, field, value):
    assert context.response.json()[field] == value

Cucumber BDD Advanced API Testing with Cucumber

Written by PV

© 2026 All Rights Reserved