Generate Cucumber feature files and Java step definitions

Paste a requirement, get a parser-validated .feature file and the Cucumber-JVM glue code to run it — laid out the way a Maven project expects. Free, no account needed to start.

Generate a Selenium WebDriver .feature file

Get valid Gherkin plus edge cases, ready to add to your Selenium WebDriver project.

How Cucumber-JVM binds a feature file to Java

Cucumber-JVM does not link scenarios to code by file name, directory, or any declaration you write. At startup it scans the packages named in its glue configuration, collects every method annotated with @Given, @When, @Then or @And, and builds a table of the expressions on those annotations. Each step in each scenario is then matched against that table at runtime.

Two consequences follow, and between them they account for most of the time people lose on their first Cucumber-JVM suite. A step definition can live in any class in any glue package and still be found — so splitting steps across files is free. And a step that matches two expressions is a hard failure (DuplicateStepDefinitionException), not a first-match-wins, so a broad expression written early will collide with a specific one written later.

The expressions themselves are Cucumber Expressions rather than regular expressions: {string}, {int}, {word} and {float} cover nearly everything, and the captured values arrive as typed method parameters. A regex still works if you anchor it with ^ and $, but you rarely need one.

Where the files go

Maven's conventions and Cucumber's classpath scan meet in a layout that is not negotiable in practice:

src
└── test
    ├── java
    │   └── steps
    │       ├── CustomerSignInSteps.java
    │       ├── RunCucumberTest.java
    │       └── World.java
    └── resources
        └── features
            └── customer-sign-in.feature
The features directory sits under test resources, not test java. This is the single most common reason a suite reports zero scenarios.

Feature files are resources. Put them in src/test/java and Maven will not copy them to the classpath, Cucumber will find nothing, and the run will pass with zero scenarios executed — a green build that tested nothing.

The JUnit 5 runner

The @RunWith(Cucumber.class) runner most tutorials still show is JUnit 4. On JUnit 5 the entry point is the cucumber-junit-platform-engine, selected from a suite class:

package steps; import org.junit.platform.suite.api.ConfigurationParameter;import org.junit.platform.suite.api.IncludeEngines;import org.junit.platform.suite.api.SelectClasspathResource;import org.junit.platform.suite.api.Suite; import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; @Suite@IncludeEngines("cucumber")@SelectClasspathResource("features")@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "steps")@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty, html:target/cucumber.html")public class RunCucumberTest {}

@SelectClasspathResource("features") points at the resources directory above; GLUE_PROPERTY_NAME names the package to scan for step definitions. Both are paths on the classpath, not file paths, which is why neither starts with src/.

Dependencies

<dependency>  <groupId>io.cucumber</groupId>  <artifactId>cucumber-java</artifactId>  <version>7.20.1</version>  <scope>test</scope></dependency><dependency>  <groupId>io.cucumber</groupId>  <artifactId>cucumber-junit-platform-engine</artifactId>  <version>7.20.1</version>  <scope>test</scope></dependency><dependency>  <groupId>io.cucumber</groupId>  <artifactId>cucumber-picocontainer</artifactId>  <version>7.20.1</version>  <scope>test</scope></dependency>

cucumber-picocontainer is optional but you will want it on the second day: it is what lets two step-definition classes share a WebDriver instance without a static field.

A worked example, end to end

The requirement below is the kind of thing that arrives in a ticket — prose, with one security constraint buried in the middle and one threshold that is easy to misread.

A registered customer signs in with their email and password. Correctcredentials take them to their dashboard. A wrong password shows an error anddoes not reveal whether the email exists. Five failed attempts in ten minuteslocks the account for an hour.

Note what the generated feature file does with "five failed attempts in ten minutes locks the account". The boundary is at five, so the Examples: table tests four and five rather than three and ten — an off-by-one here is the difference between a lock that fires early and one that never fires.

Feature: Customer sign-in   Background:    Given a registered customer "[email protected]" with password "correct-horse"   Scenario: Correct credentials reach the dashboard    Given the customer is on the sign-in page    When they sign in as "[email protected]" with password "correct-horse"    Then they should land on the dashboard   Scenario: A wrong password does not confirm the account exists    Given the customer is on the sign-in page    When they sign in as "[email protected]" with password "wrong"    Then they should see the error "Email or password is incorrect"    And the error should not mention whether the email is registered   Scenario Outline: The account locks after five failures in ten minutes    Given the customer has failed to sign in <attempts> times in the last 10 minutes    When they sign in as "[email protected]" with password "wrong"    Then the account lock state should be "<locked>"     Examples:      | attempts | locked |      | 3        | open   |      | 4        | locked |
package steps; import io.cucumber.java.en.Given;import io.cucumber.java.en.Then;import io.cucumber.java.en.When;import org.openqa.selenium.By;import org.openqa.selenium.WebDriver;import static org.junit.jupiter.api.Assertions.assertEquals;import static org.junit.jupiter.api.Assertions.assertFalse; public class CustomerSignInSteps {     private final WebDriver driver;     // PicoContainer constructs this class and hands it the same World instance    // every other step class in the scenario receives. Do not new up a driver    // here.    public CustomerSignInSteps(World world) {        this.driver = world.driver();    }     @Given("the customer is on the sign-in page")    public void theCustomerIsOnTheSignInPage() {        driver.get("https://example.com/sign-in");    }     @When("they sign in as {string} with password {string}")    public void theySignInAs(String email, String password) {        driver.findElement(By.id("email")).sendKeys(email);        driver.findElement(By.id("password")).sendKeys(password);        driver.findElement(By.cssSelector("button[type=submit]")).click();    }     @Then("they should see the error {string}")    public void theyShouldSeeTheError(String message) {        assertEquals(message, driver.findElement(By.cssSelector("[role=alert]")).getText());    }     @Then("the error should not mention whether the email is registered")    public void theErrorShouldNotLeakAccountExistence() {        String text = driver.findElement(By.cssSelector("[role=alert]")).getText().toLowerCase();        assertFalse(text.contains("no account"));        assertFalse(text.contains("not registered"));    }}
Constructor injection rather than a static driver: PicoContainer gives every step class in a scenario the same World.

Three things that will cost you an afternoon

What this tool does with a Java project

Gherkinizer takes the requirement, produces the feature file, and parses the result with the official Cucumber grammar before showing it to you — so a file that would not have loaded in your suite never reaches your clipboard. The export names files the way a Cucumber-JVM project expects them:

  • customer-sign-in.feature in src/test/resources/features
  • CustomerSignInSteps.java in src/test/java/steps
  • a README naming mvn test as the command that runs it

Java step definitions are generated on the free tier — this is the one language where you get the feature file and the glue code without an account upgrade.

Questions

Why does my Cucumber-JVM run report zero scenarios?
Almost always because the .feature files are under src/test/java instead of src/test/resources. Maven only copies resources to the classpath, and Cucumber scans the classpath, so features outside it are invisible. The build passes because nothing failed — nothing ran.
Do I need JUnit 4 or JUnit 5 for Cucumber?
JUnit 5, via cucumber-junit-platform-engine and a @Suite class with @IncludeEngines("cucumber"). The @RunWith(Cucumber.class) form in older tutorials is JUnit 4 and needs the separate cucumber-junit artifact.
How do two step-definition classes share a WebDriver?
Through dependency injection, not a static field. Add cucumber-picocontainer, put the driver on a plain World class, and take World as a constructor parameter in each step class. Cucumber creates one World per scenario and hands the same instance to every class that asks for it.
Are Cucumber Expressions the same as regular expressions?
No. {string}, {int}, {word} and {float} are Cucumber Expression placeholders and give you typed method parameters. Regular expressions still work if you anchor them with ^ and $, but you rarely need one, and mixing the two styles in a suite is what causes DuplicateStepDefinitionException.
Are Java step definitions free in Gherkinizer?
Yes. Java is the language available on the free tier, so a Cucumber-JVM project is the one stack where you can go from requirement to feature file to step definitions without upgrading.

Other frameworks