A runner class launches Cucumber from JUnit/TestNG. In JUnit it uses @CucumberOptions (or JUnit 5 @Suite properties) to configure feature paths, glue (step-def package), tags, plugins/reports, and dry-run.
A typical JUnit 4 runner: @RunWith(Cucumber.class) @CucumberOptions(features = "src/test/resources/features", glue = "stepdefs", tags = "@smoke", plugin = {"pretty", "html:target/report.html"}). Key options: features (where .feature files are), glue (where step definitions live), tags (which scenarios), plugin (reporters), dryRun (validate all steps have definitions without executing), and monochrome (clean output). JUnit 5 uses @Suite with cucumber.* properties, and TestNG extends AbstractTestNGCucumberTests. The runner is how Cucumber integrates into the build/CI; a wrong glue or features path is a frequent setup error.
A CI runner sets tags='@regression' and plugin for an HTML + JSON report, so the nightly build runs the regression subset and publishes a readable report — all configured in one runner class.
How can you quickly verify that EVERY Gherkin step in your features has a matching step definition, without running the (slow) browser tests?