JUnit 4 runner that enables executing Cucumber scenarios as JUnit tests, providing integration between Cucumber's behavior-driven development features and JUnit's testing framework
npx @tessl/cli install tessl/maven-io-cucumber--cucumber-junit@7.22.0Cucumber JUnit is a JUnit 4 runner that enables executing Cucumber scenarios as JUnit tests, providing seamless integration between Cucumber's behavior-driven development (BDD) framework and JUnit's testing infrastructure. It allows developers to execute Cucumber scenarios written in Gherkin as standard JUnit tests, making BDD practices easily accessible within existing JUnit-based testing workflows.
pom.xml:
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.22.1</version>
<scope>test</scope>
</dependency>import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;package com.example;
import io.cucumber.junit.CucumberOptions;
import io.cucumber.junit.Cucumber;
import org.junit.runner.RunWith;
@RunWith(Cucumber.class)
@CucumberOptions(plugin = "pretty")
public class RunCucumberTest {
}This creates a JUnit test runner that will execute all Cucumber scenarios in the same package as the runner class. By default, glue code (step definitions and hooks) is also assumed to be in the same package.
Cucumber JUnit is built around several key components that integrate Cucumber's BDD framework with JUnit's testing infrastructure:
Cucumber class extends JUnit's ParentRunner and orchestrates feature executionRunnerScheduler for parallel test executionThe runner follows JUnit 4's ParentRunner pattern, creating child runners for each feature file and mapping scenarios to individual JUnit test cases.
The main JUnit 4 runner class for executing Cucumber scenarios as JUnit tests.
@API(status = API.Status.STABLE)
public final class Cucumber extends ParentRunner<ParentRunner<?>> {
/**
* Constructor called by JUnit to create the Cucumber runner.
* Parses configuration options, initializes plugins, and creates feature runners.
*
* @param clazz the test class annotated with @RunWith(Cucumber.class)
* @throws InitializationError if feature parsing fails, invalid options are provided,
* or the test class contains Cucumber annotations (step definitions/hooks)
*/
public Cucumber(Class<?> clazz) throws InitializationError;
/**
* Set custom scheduler for parallel execution of feature files.
* Enables thread-safe parallel execution across multiple threads.
*
* @param scheduler the RunnerScheduler implementation to control parallel execution
*/
public void setScheduler(RunnerScheduler scheduler);
}Usage Example:
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/features",
glue = "com.example.steps",
tags = "@smoke and not @slow"
)
public class AcceptanceTest {
}Annotation for configuring Cucumber's execution options.
@API(status = API.Status.STABLE)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface CucumberOptions {
/** Skip glue code execution */
boolean dryRun() default false;
/** Feature file paths */
String[] features() default {};
/** Package paths for step definitions and hooks */
String[] glue() default {};
/** Additional glue packages */
String[] extraGlue() default {};
/** Tag expression for filtering scenarios */
String tags() default "";
/** Register plugins (junit, html, pretty, progress, json, etc.) */
String[] plugin() default {};
/** Publish reports to https://reports.cucumber.io */
boolean publish() default false;
/** Disable colored terminal output */
boolean monochrome() default false;
/** Filter scenarios by name regex */
String[] name() default {};
/** Format of generated snippets */
SnippetType snippets() default SnippetType.UNDERSCORE;
/** Use filename compatible names */
boolean useFileNameCompatibleName() default false;
/** Include steps in notifications */
boolean stepNotifications() default false;
/** Custom ObjectFactory implementation */
Class<? extends io.cucumber.core.backend.ObjectFactory> objectFactory() default NoObjectFactory.class;
/** Custom UuidGenerator implementation */
Class<? extends io.cucumber.core.eventbus.UuidGenerator> uuidGenerator() default NoUuidGenerator.class;
}Usage Examples:
Basic configuration:
@CucumberOptions(
plugin = "pretty",
monochrome = true
)Advanced configuration:
@CucumberOptions(
features = {"src/test/resources/features/login", "src/test/resources/features/checkout"},
glue = {"com.example.steps", "com.example.hooks"},
tags = "@regression and not @slow",
plugin = {
"pretty",
"html:target/cucumber-reports",
"json:target/cucumber-reports/Cucumber.json",
"junit:target/cucumber-reports/Cucumber.xml"
},
stepNotifications = true,
useFileNameCompatibleName = true
)Feature path examples:
"src/test/resources/features" - All features in directory"classpath:com/example/application" - All features in package"src/test/resources/features/example.feature:42" - Specific scenario at line 42"@target/rerun" - All scenarios in rerun filesEnum for configuring the format of generated step definition snippets.
public enum SnippetType {
/** Use underscore style for method names */
UNDERSCORE,
/** Use camel case style for method names */
CAMELCASE
}Usage Example:
@CucumberOptions(snippets = SnippetType.CAMELCASE)Cucumber JUnit supports JUnit's @ClassRule, @BeforeClass, and @AfterClass annotations. These execute before and after all scenarios:
@RunWith(Cucumber.class)
public class RunCucumberTest {
@ClassRule
public static TestRule rule = new MyTestRule();
@BeforeClass
public static void setUp() {
// Runs before all scenarios
}
@AfterClass
public static void tearDown() {
// Runs after all scenarios
}
}Note: Using JUnit rules limits portability between runners and may not execute correctly with command line, IntelliJ IDEA, or Cucumber-Eclipse. It's recommended to use Cucumber's @Before and @After hooks instead.
Cucumber JUnit supports JUnit's Assume functionality for conditional test execution:
import static org.junit.Assume.assumeTrue;
@Given("^a precondition is met$")
public void preconditionCheck() {
assumeTrue("Skipping test due to environment", isEnvironmentReady());
}Failed assumptions result in test abortion (marked as skipped) rather than failure.
Cucumber JUnit supports parallel execution of feature files across multiple threads. Configure with Maven Surefire plugin:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<parallel>both</parallel>
<threadCount>4</threadCount>
</configuration>
</plugin>The runner validates configuration and throws InitializationError for:
Common exceptions during execution:
Exception thrown when step definitions are missing for scenario steps, providing helpful snippet suggestions.
/**
* Exception thrown when Cucumber encounters undefined steps during execution.
* Contains generated code snippets to help implement missing step definitions.
*/
final class UndefinedStepException extends RuntimeException {
/**
* Creates exception with snippet suggestions for undefined steps
* @param suggestions Collection of step definition suggestions with generated code snippets
*/
UndefinedStepException(Collection<Suggestion> suggestions);
}This exception is thrown automatically by the Cucumber runner when scenarios contain steps that don't have corresponding step definitions. The exception message includes generated code snippets in the configured snippet format (underscore or camelcase).
// JUnit framework types
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerScheduler;
import org.junit.runner.RunWith;
// Cucumber JUnit types
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import io.cucumber.junit.CucumberOptions.SnippetType;
// Cucumber core types (dependencies)
import io.cucumber.core.backend.ObjectFactory;
import io.cucumber.core.eventbus.UuidGenerator;
import io.cucumber.plugin.event.SnippetsSuggestedEvent.Suggestion;
// Annotation support
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apiguardian.api.API;
// Java standard library
import java.util.Collection;