Let us build a complete, real-world feature file for the Shopping Portal checkout flow. This shows how to write comprehensive BDD scenarios for a multi-step user journey.
@shopping @checkout
Feature: Shopping Portal Checkout
As a shopper
I want to add products and complete checkout
So that I can purchase items
Background:
Given the user is on the Shopping Portal home page
@smoke
Scenario: Checkout with a single product
When the user searches for "Laptop"
And the user adds "Laptop Pro" to the cart
And the user opens the cart
Then the cart should contain 1 item
When the user proceeds to checkout
And the user fills in shipping details:
| firstName | John |
| lastName | Doe |
| email | john@doe.com |
| address | 123 Main St |
| city | Mumbai |
| zipCode | 400001 |
And the user places the order
Then the order confirmation should be displayed
@regression
Scenario: Checkout with multiple products
When the user adds "Laptop Pro" to the cart
And the user adds "Cotton T-Shirt" to the cart
And the user opens the cart
Then the cart should contain 2 items
When the user proceeds to checkout
And the user fills in shipping details:
| firstName | Jane |
| lastName | Smith |
| email | jane@smith.com |
| address | 456 Oak Ave |
| city | Delhi |
| zipCode | 110001 |
And the user places the order
Then the order confirmation should be displayed
@regression
Scenario: Cannot checkout with empty cart
When the user opens the cart
Then the cart should be empty
And the checkout button should be disabled
@regression
Scenario Outline: Add product from different categories
When the user selects category "<category>"
And the user adds the first product to the cart
And the user opens the cart
Then the cart should contain 1 item
Examples:
| category |
| Electronics |
| Clothing |
| Kitchen |package com.autopractice.stepdefinitions;
import com.autopractice.hooks.Hooks;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.*;
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.*;
import org.testng.Assert;
import java.time.Duration;
import java.util.Map;
public class ShoppingCheckoutSteps {
private WebDriver driver;
private WebDriverWait wait;
public ShoppingCheckoutSteps() {
this.driver = Hooks.getDriver();
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
@Given("the user is on the Shopping Portal home page")
public void onShoppingPortal() {
driver.get("https://www.testerrank.com/shopping");
}
@When("the user searches for {string}")
public void searchFor(String term) {
driver.findElement(By.id("search-input")).clear();
driver.findElement(By.id("search-input")).sendKeys(term);
driver.findElement(By.id("search-btn")).click();
}
@When("the user adds {string} to the cart")
public void addToCart(String productName) {
By addBtn = By.xpath(
"//div[contains(@class,'product-card')]" +
"[.//span[text()='" + productName + "']]" +
"//button[contains(@class,'add-to-cart')]");
wait.until(ExpectedConditions.elementToBeClickable(
addBtn)).click();
}
@When("the user opens the cart")
public void openCart() {
driver.findElement(By.id("cart-icon")).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.cssSelector(".cart-container")));
}
@Then("the cart should contain {int} item(s)")
public void cartContains(int expected) {
int actual = driver.findElements(
By.cssSelector(".cart-item")).size();
Assert.assertEquals(actual, expected,
"Cart count mismatch");
}
@Then("the cart should be empty")
public void cartEmpty() {
Assert.assertTrue(
driver.findElement(
By.cssSelector(".empty-cart")).isDisplayed());
}
@Then("the checkout button should be disabled")
public void checkoutDisabled() {
Assert.assertFalse(
driver.findElement(
By.id("checkout-btn")).isEnabled());
}
@When("the user proceeds to checkout")
public void proceedToCheckout() {
driver.findElement(By.id("checkout-btn")).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("first-name")));
}
@When("the user fills in shipping details:")
public void fillShipping(DataTable dataTable) {
Map<String, String> data = dataTable.asMap(
String.class, String.class);
driver.findElement(By.id("first-name"))
.sendKeys(data.get("firstName"));
driver.findElement(By.id("last-name"))
.sendKeys(data.get("lastName"));
driver.findElement(By.id("email"))
.sendKeys(data.get("email"));
driver.findElement(By.id("address"))
.sendKeys(data.get("address"));
driver.findElement(By.id("city"))
.sendKeys(data.get("city"));
driver.findElement(By.id("zip-code"))
.sendKeys(data.get("zipCode"));
}
@When("the user places the order")
public void placeOrder() {
driver.findElement(By.id("place-order-btn")).click();
}
@Then("the order confirmation should be displayed")
public void orderConfirmed() {
WebElement confirmation = wait.until(
ExpectedConditions.visibilityOfElementLocated(
By.id("order-confirmation")));
Assert.assertTrue(confirmation.isDisplayed());
}
@When("the user selects category {string}")
public void selectCategory(String category) {
driver.findElement(By.linkText(category)).click();
}
@When("the user adds the first product to the cart")
public void addFirstProduct() {
wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector(
".product-card .add-to-cart"))).click();
}
}Notice how the feature file reads like a user story. A product manager can review it and say "yes, this is our checkout flow" or "wait, we also need to verify the total before placing the order." BDD makes feature files living documentation that the whole team owns.
Key Point: A complete Cucumber test: feature file with Background, Scenario, Scenario Outline, Data Tables, and tags. Step definitions call page objects.