Step Definitions

Gluing Steps to Code

Each Gherkin step is matched to a Java method by a Cucumber expression. {string} captures a quoted value, {int} a whole number. Step methods call page objects; they should stay thin.

package com.shopease.steps;

import io.cucumber.java.en.*;

public class LoginSteps {
    private final LoginPage loginPage = new LoginPage(DriverFactory.getDriver());
    private HomePage homePage;

    @Given("the user is on the login page")
    public void onLoginPage() {
        loginPage.open();
    }

    @When("the user logs in with {string} and {string}")
    public void logsIn(String email, String password) {
        homePage = loginPage.loginAs(email, password);
    }

    @Then("the welcome message {string} is shown")
    public void welcomeShown(String expected) {
        Assert.assertEquals(homePage.welcomeText(), expected);
    }

    @Then("the error {string} is shown")
    public void errorShown(String expected) {
        Assert.assertEquals(loginPage.errorText(), expected);
    }
}

How Matching Works

Example

Cucumber turns each expression into a regular expression and extracts the arguments. This mini matcher does the same for {string} and {int}:

import java.util.*;
import java.util.regex.*;

public class Main {
    static Pattern compile(String expression) {
        String regex = expression
                .replace("{string}", "\"([^\"]*)\"")
                .replace("{int}", "(-?\\d+)");
        return Pattern.compile("^" + regex + "$");
    }

    public static void main(String[] args) {
        Map<String, Pattern> steps = new LinkedHashMap<>();
        for (String e : List.of("the user logs in with {string} and {string}",
                                "the user adds {int} items to the cart"))
            steps.put(e, compile(e));

        List<String> gherkin = List.of(
                "the user logs in with \"[email protected]\" and \"Secret@123\"",
                "the user adds 3 items to the cart",
                "the user pays with UPI");

        for (String line : gherkin) {
            boolean found = false;
            for (var entry : steps.entrySet()) {
                Matcher m = entry.getValue().matcher(line);
                if (m.matches()) {
                    List<String> argsFound = new ArrayList<>();
                    for (int i = 1; i <= m.groupCount(); i++) argsFound.add(m.group(i));
                    System.out.println("MATCH  " + entry.getKey() + "  args=" + argsFound);
                    found = true;
                }
            }
            if (!found) System.out.println("UNDEFINED  " + line);
        }
    }
}

Output

MATCH  the user logs in with {string} and {string}  args=[[email protected], Secret@123]
MATCH  the user adds {int} items to the cart  args=[3]
UNDEFINED  the user pays with UPI

Undefined Steps

Example

When no method matches a step, Cucumber prints a snippet you can paste:

mvn test -Dcucumber.features=src/test/resources/features/checkout.feature

Example output

Undefined scenarios:
file:///D:/shopease-ui-tests/src/test/resources/features/checkout.feature:7 # Pay with UPI

1 Scenarios (1 undefined)
4 Steps (2 skipped, 1 undefined, 1 passed)

You can implement missing steps with the snippets below:

@When("the user pays with UPI")
public void the_user_pays_with_upi() {
    // Write code here that turns the phrase above into concrete actions
    throw new io.cucumber.java.PendingException();
}