Answer
Cucumber + REST Assured for API BDD Testing
Dependencies
XML
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.15.0</version>
<scope>test</scope>
</dependency>
Feature File — API in Gherkin
Gherkin
Feature: User Management API
@api @smoke
Scenario: Create a new user via POST
Given the API base URL is "https://api.example.com"
When I send a POST request to "/users" with body:
| name | John Doe |
| email | john@example.com |
| role | USER |
Then the response status code should be 201
And the response body should contain "userId"
And the response "name" field should equal "John Doe"
@api @regression
Scenario: Get user by ID
Given the API base URL is "https://api.example.com"
And a user with ID "101" exists
When I send a GET request to "/users/101"
Then the response status code should be 200
And the response "email" field should equal "john@example.com"
@api @negative
Scenario: Create user with missing email returns 400
Given the API base URL is "https://api.example.com"
When I send a POST request to "/users" with body:
| name | Jane Doe |
Then the response status code should be 400
And the response "error" field should contain "email is required"
Step Definitions with REST Assured
Java
package stepDefinitions;
import io.cucumber.java.en.*;
import io.cucumber.datatable.DataTable;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import static org.testng.Assert.*;
public class ApiSteps {
private Response response;
private RequestSpecification request;
private String baseUrl;
@Given("the API base URL is {string}")
public void setBaseUrl(String url) {
this.baseUrl = url;
RestAssured.baseURI = url;
request = given()
.header("Content-Type", "application/json")
.header("Accept", "application/json");
}
@When("I send a POST request to {string} with body:")
public void sendPostRequest(String endpoint, DataTable dataTable) {
Map<String, String> body = dataTable.asMap(String.class, String.class);
response = request
.body(body)
.when()
.post(endpoint)
.then()
.extract().response();
}
@When("I send a GET request to {string}")
public void sendGetRequest(String endpoint) {
response = request
.when()
.get(endpoint)
.then()
.extract().response();
}
@Then("the response status code should be {int}")
public void verifyStatusCode(int expectedCode) {
assertEquals(expectedCode, response.getStatusCode(),
"Status code mismatch. Body: " + response.getBody().asString());
}
@Then("the response body should contain {string}")
public void verifyBodyContains(String key) {
assertNotNull(response.jsonPath().get(key),
"Key '" + key + "' not found in response");
}
@Then("the response {string} field should equal {string}")
public void verifyFieldValue(String field, String expected) {
String actual = response.jsonPath().getString(field);
assertEquals(expected, actual);
}
@Then("the response {string} field should contain {string}")
public void verifyFieldContains(String field, String expected) {
String actual = response.jsonPath().getString(field);
assertTrue(actual.contains(expected));
}
}
API Context for Chaining Scenarios
Java
// Share response data between steps within one scenario
public class ApiContext {
public Response lastResponse;
public String extractedId;
public String authToken;
public Map<String, String> requestHeaders = new HashMap<>();
}
