Skip to main content

Implementing Steps

A fully working example — you extend the generated abstract class in a concrete subclass, implement each step method with real assertions, and the tests run green.

Source: examples/getting-started/example-3 on GitHub.

What this demonstrates

  • The end-to-end workflow from spec file to passing tests
  • The default abstract mode: the generated …Scenarios class is abstract, and you implement its step methods in a concrete subclass
  • State management via plain instance fields on the subclass — no DI framework needed
  • Real JUnit assertions in step methods
  • Any step method you forget to implement is a compile error, not a runtime failure

The workflow

  1. Create a marker class annotated with @Gherkin2JUnit("…").
  2. Compile — the generator emits an abstract class …Scenarios with one abstract method per step and one @Test method per scenario.
  3. Create a concrete subclass that extends the generated …Scenarios class and implement every abstract step method — this is where your real test code and shared state live.
  4. JUnit discovers and runs the concrete subclass; any unimplemented step won't compile.

Steps shared across many features can instead be implemented on the marker class itself — the generator inherits them and skips the abstract declaration. To make the generated class runnable without a subclass, see Concrete Mode.

Implementing the steps

The concrete subclass extends the generated class and holds shared state in plain instance fields:

public class ShoppingCartTest extends ShoppingCartScenarios {

private final List<CartItem> cart = new ArrayList<>();

@Override
public void iHaveAnEmptyShoppingCart() {
cart.clear();
}

@Override
public void iAdd$p1WithQuantity$p2AndUnitPrice$p3(String name, Integer quantity, Double unitPrice) {
cart.add(new CartItem(name, quantity, unitPrice));
}

@Override
public void theCartSubtotalShouldBe$p1(Double expectedSubtotal) {
double subtotal = cart.stream()
.mapToDouble(item -> item.quantity() * item.unitPrice())
.sum();
assertEquals(expectedSubtotal, subtotal, 0.001);
}

record CartItem(String name, int quantity, double unitPrice) {}
}

Parameter names in your implementation can be anything — only the method name and parameter types need to match the generated signature.

Run it

cd examples/getting-started/example-3
mvn test