Skip to main content

Organizing Steps into Interfaces

Instead of declaring every step method on one class, group them into domain interfaces and have the marker class implement those interfaces. The generator inherits the step methods through the interfaces — no abstract declarations, no per-feature glue.

Source: examples/going-further/example-3 on GitHub.

What this demonstrates

  • Step methods grouped by domain into interfaces (CartSteps, CheckoutSteps)
  • Interfaces carry shared implementations via Java default methods
  • The marker class implements the interfaces to pull in all their steps
  • The generator sees inherited step methods and emits no abstract declarations for them
  • A single marker can compose any number of step interfaces

The step-interface pattern

Organise step methods by domain area using interfaces with default methods:

public interface CartSteps {
default void iHaveAnEmptyShoppingCart() { /* shared cart implementation */ }
default void iAdd$p1ToTheCart(String item) { /* shared cart implementation */ }
}

public interface CheckoutSteps {
default void iProceedToCheckout() { /* shared checkout implementation */ }
default void iPayWithCard$p1(String cardNumber) { /* shared checkout implementation */ }
default void theOrderShouldBeConfirmed() { /* shared checkout implementation */ }
}

Then the marker class implements every interface:

@Gherkin2JUnit("specs/ShoppingCart.specb")
public abstract class ShoppingCartFeature implements CartSteps, CheckoutSteps {
}

What gets generated

The generated ShoppingCartScenarios inherits all step methods through the marker, so none of them are re-declared as abstract:

public abstract class ShoppingCartScenarios extends ShoppingCartFeature {

// No abstract step methods — every step is inherited from
// CartSteps / CheckoutSteps via ShoppingCartFeature.

@Test
@DisplayName("Scenario: Add an item and check out")
public void scenario_1() {
/*
* Given I have an empty shopping cart
*/
iHaveAnEmptyShoppingCart();
// … remaining step calls …
}
}

Why organize this way

  • Cohesion — cart steps live with cart steps, checkout steps with checkout steps
  • Reuse — the same interface can back many markers (pair this with Glob Patterns to share one vocabulary across many features)
  • Composition — mix and match interfaces per marker instead of one monolithic step class

Run it

cd examples/going-further/example-3
mvn test