Skip to main content

Cucumber Annotations

Two related capabilities: generating @Given / @When / @Then Cucumber annotations on step methods, and using those annotations to match steps to implementations by pattern instead of by method name.

Why this mode exists — migrating from Cucumber

Annotation-based matching is not how you would set up a new suite: SpecBinder matches on method names by default, and that needs no annotations at all. This mode is here so an existing Cucumber suite can move across. Your current @Given / @When / @Then step definitions are matched by their annotation patterns, so they can be reused as they are, without renaming methods to fit SpecBinder's naming convention.

Migrating from Cucumber covers the rest of what changes on the way over.

Source: examples/migrating-from-cucumber/example-2 on GitHub.

What this demonstrates​

1. Generating Cucumber annotations​

  • @Gherkin2JUnitOptions(addCucumberStepAnnotations = true) adds annotations from io.cucumber.java.en.*
  • Each step method the generator declares receives a @Given / @When / @Then with a pattern matching the original Gherkin step text
  • And / But steps inherit the keyword from the preceding Given / When / Then
  • Requires the cucumber-java dependency

2. Annotation-based step matching​

  • useCucumberAnnotationsForStepMatching defaults to false; enable it with @Gherkin2JUnitOptions(useCucumberAnnotationsForStepMatching = true) (as this example does)
  • When the generator looks for steps already declared in the marker hierarchy, it matches by annotation pattern, not method name
  • You can use any method name you like — as long as the annotation pattern matches the Gherkin step text, the generator calls that method from the scenario and won't emit a declaration of its own
  • The matched methods can be abstract: here the marker declares them and the concrete ShoppingCartTest implements them

3. Two pattern styles work​

The annotation value can be either:

  • A Cucumber expression — e.g. @When("I apply discount code {string}")
  • A regular expression — e.g. @When("^I add \"([^\"]*)\" to the cart$")

Both are equally valid for matching, and you can mix the two within a single class.

Custom method names with annotation matching​

The marker class declares five of the six steps, as abstract methods with descriptive names instead of the ones SpecBinder would derive:

Gherkin stepCustom method namePattern styleAnnotation
Given I have an empty shopping cartstartWithEmptyCart()Cucumber expression@Given("I have an empty shopping cart")
When I add "..." to the cartaddItemToCart()Regular expression@When("^I add (?<p1>.*) to the cart$")
Then the cart should contain "..." itemsverifyCartSize()Regular expression@Then("^the cart should contain (?<p1>.*) items$")
Given I have a cart with subtotal "..."setupCartWithSubtotal()Regular expression@Given("^I have a cart with subtotal (?<p1>.*)$")
When I apply discount code "..."applyDiscount()Cucumber expression@When("I apply discount code {string}")
@Gherkin2JUnitOptions(addCucumberStepAnnotations = true, useCucumberAnnotationsForStepMatching = true)
@Gherkin2JUnit
public abstract class ShoppingCartFeature {

@Given("I have an empty shopping cart")
public abstract void startWithEmptyCart();

@When("^I add (?<p1>.*) to the cart$")
public abstract void addItemToCart(String item);

// ...
}

The bare @Gherkin2JUnit picks up ShoppingCart.feature, which sits next to the marker in src/test/java and keeps its .feature extension — the file a Cucumber suite already has.

Generated output​

The sixth step, Then the cart subtotal should be "...", is deliberately left undeclared. The generator calls the five matched methods from the scenarios and emits the missing one itself — and because addCucumberStepAnnotations is on, with a generated @Then:

public abstract class ShoppingCartScenarios extends ShoppingCartFeature {

@Test
@DisplayName("Scenario: Add item and verify cart")
public void scenario_1() {
startWithEmptyCart();
addItemToCart("Wireless Headphones");
addItemToCart("Coffee Beans");
verifyCartSize(2);
}

@Then("^the cart subtotal should be (?<p1>.*)$")
public abstract void theCartSubtotalShouldBe$p1(Double p1);

@Test
@DisplayName("Scenario: Apply discount code")
public void scenario_2() {
setupCartWithSubtotal(100.00);
applyDiscount("SAVE10");
theCartSubtotalShouldBe$p1(90.00);
}
}

The concrete ShoppingCartTest implements all six — the five custom-named declarations and the generated one.

Class hierarchy​

ShoppingCartFeature.java          (marker — custom-named, annotated step declarations)
└→ ShoppingCartScenarios.java (generated, abstract — calls them, adds the one missing step)
└→ ShoppingCartTest.java (your concrete class, implements all steps)

Supported parameter types in Cucumber expressions​

{int}, {long}, {short}, {byte}, {float}, {double}, {bigdecimal}, {biginteger}, {word}, {string}, and the anonymous {}. Custom parameter types are not currently supported.

Run it​

cd examples/migrating-from-cucumber/example-2
mvn test