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.
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 fromio.cucumber.java.en.*- Each step method the generator declares receives a
@Given/@When/@Thenwith a pattern matching the original Gherkin step text And/Butsteps inherit the keyword from the precedingGiven/When/Then- Requires the
cucumber-javadependency
2. Annotation-based step matching
useCucumberAnnotationsForStepMatchingdefaults tofalse; 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
ShoppingCartTestimplements 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 step | Custom method name | Pattern style | Annotation |
|---|---|---|---|
Given I have an empty shopping cart | startWithEmptyCart() | Cucumber expression | @Given("I have an empty shopping cart") |
When I add "..." to the cart | addItemToCart() | Regular expression | @When("^I add (?<p1>.*) to the cart$") |
Then the cart should contain "..." items | verifyCartSize() | 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