Release Notes
What changed in each release of the SpecBinder framework. Each version links to its page on Maven Central.
Release notes for the IDE plugin live under IDE Plugin → Release Notes.
2026.47.0Latest
Added
- Execution reporter: re-running only part of a spec — a single scenario, a single Rule, or one example row of a Scenario Outline — now folds that run's results into the per-feature JSON report already on disk instead of replacing the file. Previously any partial run left behind a report describing only what had just run, discarding every other scenario's outcome, so keeping a complete picture meant re-running the whole spec every time. A scenario that did not run keeps the outcome of its last execution, and the report's summary, start time and total duration are re-derived from everything the merged report holds. Results are matched to scenarios by position and by content together, because neither survives editing on its own: a scenario that moved keeps its outcome, one whose own steps changed keeps the hash recorded against it so a consumer can still tell the outcome describes an older version, and one deleted from the spec is dropped rather than lingering as a failure nothing can clear. Example rows are matched on their values rather than their place in the
Examplestable, so reordering the table does not shuffle outcomes between rows, and a row no longer in the table takes its result with it; the merged rows are laid out in the order the table now reads. Merging happens only when the file on disk fits the run — readable, describing this same feature, and carrying the current report schema version — so a stale or foreign report is replaced rather than allowed to contribute results. A full run needs no special handling and produces exactly what it did before
Changed
@Gherkin2JUnitOptions(emitScenarioHash = ...)now defaults totrue, so every generated@Testand@ParameterizedTestmethod carries a@ScenarioHashannotation unless the option is explicitly set tofalse. The option had quietly grown into far more than its name suggests: with it switched off the execution report also loses the original Gherkin step text, a Scenario Outline's template steps and its typed step arguments, and any way to tell an outcome recorded against an older version of a scenario from one describing the spec as it stands today — so the previous default handed most projects a materially thinner report than they had any reason to expect. Existing projects need no source change and the generated tests behave exactly as before; only something pinning the exact text of a generated class or the exact JSON of an execution report will notice the added annotation and the richer report. SetemitScenarioHash = falseon the marker class to keep the previous output. The processor's verbose startup log reports the new default accordingly
2026.46.0
Added
- (experimental) New opt-in
@Gherkin2JUnitOptions(stripBetweenPatterns = {...})option that declares the two ends of a span of spec file text as separate patterns —@StripBetween(start = "...", end = "...")— and strips everything from the start marker to the end marker, markers included. It is the safer companion tostripPatternsfor deleting text retired by an issue, such as<REMOVED BR-789>legacy discount </REMOVED BR-789>. Expressing the same span as a single regex has three traps that each produce a successful build with quietly wrong output: forgetting the dot-all flag makes a span crossing a newline silently fail to match; a greedy quantifier makes the span run to the last closing marker in the file, deleting everything in between; and an unclosed marker simply does not match. Declaring the ends separately removes the first two entirely, because the span is located by position rather than by one regex — so neither pattern needs a flag to cross lines, and each start always pairs with the nearest following end. A span may wrap whole Gherkin constructs such as several steps, an entire Scenario, or table rows; nesting is not supported, and a start with no following end leaves the text untouched. These pairs are applied beforestripPatterns, so a span still disappears wholesale even when a pattern there would also have matched its markers individually; the default is an empty list, which leaves the spec file untouched
2026.45.0
Added
- Execution reporter: a Scenario Outline in the per-feature JSON report now carries a
templateStepsarray on the outline node — a single view of the outline's own steps with their<>placeholders intact, each entry pairing the calledmethodNamewith the original Gherkintext. A template step whose spec has a trailing DocString or DataTable also carries it as a typedargumentsentry in template form — placeholders left unresolved, and DataTables keyed by their column headers without the runtimecolumnsmapping since no row object is bound to the template. This complements the per-rowexamples, which resolve those placeholders to concrete values, so consumers can render the outline template once instead of re-deriving it from every row. Gated on the same scenario-hash match as steptext; omitted when the hash is absent or the spec has been edited since the test was generated - Execution reporter: a Scenario Outline in the per-feature JSON report now carries an aggregate
statuson the outline node, rolled up from its example rows worst-first — failed if any row failed, else aborted if any row aborted, else skipped if any row was skipped, else passed. Previously the outline node had no status of its own, so consumers had to derive one from the rows; an outline whose rows all passed could be shown as not-executed - (experimental) New opt-in
@Gherkin2JUnitOptions(stripPatterns = {...})option that strips matching text out of a spec file before it is turned into test code. Teams that annotate specs with revision markers tying wording back to an issue tracker — for example<CHANGED BR-123>premium</CHANGED BR-123>or<REMOVED BR-789>legacy discount </REMOVED BR-789>— previously found those markers reaching the generated code: they became part of step method names, so adding or editing a marker renamed an abstract step method and broke the hand-written test class implementing it, and they corrupted field names derived from data table headers and emitted unbalanced HTML into JavaDoc. Every match of every configured regular expression is removed, so the shape of the pattern decides what disappears: match only a marker and the text it wrapped survives, match an opening marker through a closing marker and the wrapped text goes with it. Text is stripped everywhere it can appear — step text, Feature/Rule/Scenario names, descriptions, doc strings, data tables andExamplestables, including header cells. A pattern may wrap whole Gherkin constructs such as several steps, an entire Scenario, or table rows; any line left holding only whitespace is dropped, so removing a table row does not leave a gap that would break the table, though this shifts the source line numbers of everything below it. Patterns are applied in the order declared, which matters when they overlap — a marker-only pattern listed first can strip the markers a wrapping pattern was relying on, so list wrapping patterns first. A match that takes all of a step’s text but leaves its keyword behind fails the build with a message naming the line; the default is an empty list, which leaves the spec file untouched
Changed
- The
@BeforeEachmethod generated from a feature-levelBackgroundis now namedbackground(previouslyfeatureBackground). Rule-level background methods are unchanged (ruleBackground) - Internal terminology cleanup: the annotation processor's internal identifiers and comments that still used the old "inject" wording were renamed to "resolved" (e.g. the trailing parameters propagated from a matched base step method), completing the alignment behind the earlier
@JUnitInject→@JUnitResolvedrename. No behavior or public API change - ⚠️ BREAKING: Scenario Outline example rows are now named
Example: [...]instead ofExample 1: [...]— the row number was dropped from the generated@ParameterizedTestdisplay name, so IDE/CI test output and thedisplayNamein execution reports no longer include it. Tooling that matches on theExample N:prefix must be updated; rows remain distinguishable by their[argument = value, ...]suffix
Fixed
- Execution reporter: per-step outcomes are now captured for tests that use a per-class test-instance lifecycle (JUnit's
@TestInstance(Lifecycle.PER_CLASS), such as a base class with a non-static@BeforeAll). Previously every step in such a feature was reported asskippedin the JSON report even when it actually ran and passed or failed; now each step's true status, timing, and error are recorded just as they are under the default per-method lifecycle
Removed
- ⚠️ BREAKING: Execution reporter: the
sourceLinefield has been removed from every node of the per-feature JSON report — scenarios, Scenario Outlines, example rows and Rules. The field could only ever be populated when the generated test code carried a source-line marker, which generation does not produce, so in practice it was absent from real reports and present only in hand-written test classes. Scenarios and Rules are no longer re-sorted by source line either; they now appear in the order they executed, which for generated code is spec order. Tooling readingsourceLineshould locate a scenario by itsscenarioHashor itsidinstead
2026.44.0
Added
- The
@Gherkin2JUnitpath now accepts a leading./to anchor matching at the annotated class's own package directory instead of the classpath root — for both glob patterns (e.g."./**/*.feature") and individual files (e.g."./Cart.feature"). This lets a marker class pick up the feature files sitting alongside it without repeating its full package path; generated class names and packages still mirror each matched file's real location. A./pattern that matches nothing reports the pattern exactly as written - New opt-in
@Gherkin2JUnitOptions(skipUnchangedSpecs = true)flag that skips regenerating a test class when none of its generation inputs have changed since it was last generated — cutting wasted work on incremental builds for projects with many feature files. The generator stamps each generated class with a@SourceTimestamprecording the newest last-modified time across the spec file, the marker class, and the marker's class hierarchy (where options typically live, so editing options forces regeneration); on a later run it re-stamps only when that value has advanced. A previously generated class that is missing or carries no recorded timestamp — for example after a clean build — always regenerates, so the optimization never leaves stale output behind. Detection follows the newest input time, so a change that does not advance it (such as agit checkoutof an older revision) is not picked up; the default remainsfalse
Changed
@Gherkin2JUnitOptions(skipGenerationForTags = ...)now keeps tagged Scenarios and Rules visible in the generated test class as skipped tests instead of omitting them entirely: the@Testmethod is still generated with its Gherkin steps rendered as a comment, and its body reports the test as skipped viaAssumptions.assumeTrue(false, ...); a tagged Rule's nested class is generated with each of its scenarios skipped this way. A tagged Feature generates no test class at all — the whole feature is dropped from generation. This surfaces manual or work-in-progress items as skipped in test reports rather than silently disappearing from the suite- The
@ScenarioHashvalue now ignores inter-word whitespace within a step, so steps that differ only in the number of spaces between words produce the same hash — matching the existing behavior where blank lines between steps and the step keyword (Given/When/Then/And/But/*) already have no effect. Downstream tooling comparing hashes against live source no longer sees spurious changes from cosmetic re-spacing
Fixed
- Execution reporter: a hand-written
@BeforeEachlifecycle method on a base or marker class (for example asetUp()that builds a test fixture) is no longer mistaken for a GherkinBackground. Only@BeforeEachmethods on SpecBinder-generated test classes contribute background steps now, so infrastructure setup — and any non-serializable values it passes around — no longer leak into the per-feature JSON report as spurious background steps or step arguments - Execution reporter: the per-feature JSON report is now written atomically — serialized in full, then moved into place — so a failure partway through serialization can no longer leave a truncated, unparseable report on disk. A previously written valid report survives intact, and a consumer reading the file never observes a partially written report
- Gherkin description text (under a Feature, Rule, Scenario, or Background) that contains a
*/sequence — for example a glob pattern such as**/*.feature— no longer produces uncompilable generated test code. Such descriptions are rendered as a JavaDoc comment by default, and the sequence previously closed that comment early; it is now emitted so it stays inside the comment and reads as written
2026.43.0
Added
- Execution reporter: each step in the per-feature JSON report now carries a
publishedReporterEntriesarray ofTestReporter.publishEntry(...)calls made from that step's code, each with its key, value, and timestamp — so structured per-step diagnostics flow through to downstream tooling alongside status and timing - Execution reporter: each step in the per-feature JSON report now carries a
textfield with the original Gherkin line (keyword + spec text verbatim) for Backgrounds, scenarios, and Scenario Outline example rows — so consumers can render a readable Given/When/Then trace without re-parsing the.featurefile; omitted when the spec has been edited since the test was generated - Execution reporter: each entry of a step's
argumentsarray now self-describes its Gherkin kind via a{type, value}envelope —typeis one ofsimple,docString, ordataTable. DocString entries additionally carry the opening-fence media-type identifier (e.g.htmlfrom"""html) on amediaTypefield. DataTable entries additionally carry acolumnsarray of{header, field}pairs in source-file column order, so consumers know both the spec-verbatim column heading and the JSON key used in each row — useful when SpecBinder rewrites multi-word headers likeUser NametouserName. Gated on the same scenario-hash match astextstamping; on a mismatch or absent hash,argumentsstay as today's bare runtime values - New opt-in
@Gherkin2JUnitOptions(descriptionAsAnnotation = true)flag that emits Gherkin description text (under Feature / Rule / Scenario / Background) as a runtime-retained@Description("""…""")annotation on the corresponding generated class or method — placed immediately below@DisplayName— instead of the default JavaDoc block, so downstream tooling can read the description at runtime via reflection. The two emission modes are mutually exclusive; the default remains JavaDoc and existing projects see no change - Execution reporter: when
descriptionAsAnnotationis enabled, the per-feature JSON report now carries the Gherkin description text on a newdescriptionfield at the Feature, Rule, and Scenario level (including the Scenario Outline parent — example rows share the parent's description) — so consumers can render the full Gherkin context next to each heading without re-parsing the.featurefile; omitted when the option is off or the element has no description - Execution reporter: steps marked
skipped(because an earlier step in the same scenario failed) now also carry theirargumentsarray in the per-feature JSON report, populated from the parsed.feature/.specbsource instead of from a runtime call — so consumers can render the same DocString body, DataTable rows, and quoted inline values they would have seen had the step actually run. Excludes Scenario Outline example rows, whose spec text carries<placeholder>tokens rather than per-example values; gated on the same scenario-hash match astextstamping - DocStrings that would exceed the JVM's per-string-literal byte limit (65535 bytes for one constant-pool entry) are now automatically emitted as multiple plain string-literal chunks joined at runtime via
String.join("", …), so generated test classes compile even when an individual DocString — e.g. a base64-encoded fixture or snapshot image — is very large. The+operator can't serve this purpose because javac compile-time-folds adjacent string-literal+expressions back into a single constant-pool entry;String.joindefers concatenation to runtime so each chunk stays its own constant. Below the configurable cap the existing single Java text-block emission is preserved unchanged. A new@Gherkin2JUnitOptions(maxStringLiteralBytes = N)option configures the cap (default 65000, just under the JVM hard limit); tests can lower it to exercise the chunking with small inputs
Changed
- ⚠️ BREAKING: The
@JUnitInjectmarker annotation has been renamed to@JUnitResolved, aligning with JUnit's own "parameter resolution" terminology (itsParameterResolverextension point) rather than implying dependency injection. Its behavior is unchanged — mark a custom resolver-supplied step-method parameter, or its type, so the parameter is propagated to the generated test method. Update anyimport dev.specbinder.annotations.JUnitInject;and@JUnitInjectusages to the new name - Class-level
@DisplayNameon the generated test class now reflects the Feature title line (e.g.,Feature: Shopping Cart) instead of the annotated class name, matching the convention already used forRule:andScenario:@DisplayNamevalues; whenaddSourceLineNumbersis enabled the line number is included asFeature [N]: .... The class-level JavaDoc now contains only the feature description lines and is omitted entirely when the feature has no description, so reports and generated code read consistently with the Gherkin source - Execution reporter: the per-feature JSON report's top-level
displayNamenow reflects the Gherkin Feature title regardless of whether SpecBinder runs in concrete or abstract generation mode — in abstract mode the reporter now walks the JUnit test class's superclass chain to read the generated class's@DisplayNamerather than falling back to the user-written concrete subclass's simple name - Execution reporter: the per-feature JSON report's
generatedClassfield has been renamed totestClass, to accurately reflect that it names the test class that was actually run — which is not always the same as the generated class - Execution reporter: per-feature JSON report's
schemaVersionbumped from 6 to 8 to reflect the new typed-arguments envelope shape and the renamedtestClassfield - ⚠️ BREAKING: The default value of
@Gherkin2JUnitOptions(useCucumberAnnotationsForStepMatching = ...)has been flipped fromtruetofalse. By default the generator now matches existing step implementations in the base/marker class by method name only, ignoring@Given/@When/@Thenannotation values. Projects that relied on annotation-pattern matching — typically when migrating from Cucumber and keeping descriptive method names — must now opt back in with@Gherkin2JUnitOptions(useCucumberAnnotationsForStepMatching = true), otherwise those steps will no longer be recognised as implemented and will be (re)generated
Fixed
- Execution reporter: per-step
argumentsno longer accumulate nested{type, value, …}wrappers when the feature has Gherkin Rules — previously each Rule's@NestedJUnit class triggered an extra finalisation pass that re-wrapped the already-wrapped arguments, producing JSON likevalue: {type: docString, value: {type: docString, value: …}}with depth equal to (number of Rules + 1)
2026.42.0
Added
- Propagation of JUnit 5's built-in injected parameters (
TestInfo,TestReporter,@TempDir Path/@TempDir File) from base step methods through to the generated@Test/@BeforeEach/@ParameterizedTestmethods — declared on the step method signature in the base/marker class, they are auto-forwarded on the step call and aggregated (deduplicated by name) across all steps of the enclosing scenario or background, with@TempDirand any other parameter annotations preserved verbatim on the generated method parameter - New
@JUnitInjectmarker annotation (@Target({PARAMETER, TYPE})) that extends the same propagation to custom user-defined types resolved by a JUnitParameterResolver— placed on the parameter directly or once on the type's class declaration; works in concrete and abstract generation modes, in scenarios, scenario outlines, and backgrounds; all non-@JUnitInjectannotations on the source parameter are preserved verbatim so the user's resolver can observe them at runtime
2026.39.0
Added
- Automatic wrapping of Gherkin values in a domain value object's static factory method when the target parameter or DataTable column maps to a non-enum custom type — covers quoted step parameters and
LIST_OF_OBJECT_PARAMScell values, including overload disambiguation by inferred value type with a unique-String-factory fallback
Changed
- Default value of
@Gherkin2JUnitOptions.shouldBeAbstractflipped fromfalsetotrue, making abstract test class generation the new default — opt back into concrete generation with@Gherkin2JUnitOptions(shouldBeAbstract = false)
2026.38.0
Added
- Execution reporter: a JUnit 5 extension that captures hierarchical execution results (feature, rule, scenario, step) and writes per-feature JSON reports to
target/specbinder-reports/ - Step-level reporting via ByteBuddy bytecode instrumentation — intercepts step method calls at runtime to track individual step status, timing, and errors without requiring source-level changes
- Bytecode call-site scanner that discovers ordered step method sequences from generated test class bytecode
Changed
- Scenario test methods inside rule nested classes are now named
rule_N_scenario_Minstead ofscenario_Mto include the rule index prefix
Fixed
- DataTable object type naming when step text ends with non-alphanumeric characters (e.g.,
"Given the following reports :"with a trailing space before the colon)
2026.37.0
Changed
- automated tests setup
- Simplified GitHub Release creation step in the release workflow
2026.33.0
Changed
- Upgraded GitHub Actions to v5 to resolve Node.js 20 deprecation warnings
Fixed
- Excluded auto-generated source code archives from GitHub Releases
2026.32.0
Changed
- Upgraded GitHub Actions to v5 to resolve Node.js 20 deprecation warnings
Fixed
- Excluded auto-generated source code archives from GitHub Releases
2026.31.0
Fixed
- Fixed GitHub Release creation failing due to shell interpretation of backticks in changelog notes
2026.30.0
Added
- Support for escaping spaces and backslashes inside DocString step argument types
Changed
- Adopted calendar year as the major version in the versioning scheme (e.g.,
2026.30.0) - Excluded source code archives from GitHub Releases
0.29.0
Fixed
- Fixed release profile running on child modules by adding
inherited=false
0.28.0
Added
- Support for escaping spaces and backslashes in Gherkin elements
- Automated release workflow via GitHub Actions triggered by
rctag push - Changelog stamping as part of the release process
Changed
- Moved to automated versioning process with minor version increments