Skip to content

[Feature Request] BDD / Gherkin Reporting Improvements β€” Real-World Pain Points from Parallel Reqnroll + NUnit ProjectΒ #238

Description

@Moshex

Hi πŸ‘‹ β€” we're actively using ExtentReports v5.0.4 for BDD test reporting in a .NET 9 MAUI project with Reqnroll (SpecFlow successor) + NUnit, running parallel Android/iOS tests. We've built a ~667-line ExtentReportManager.cs wrapper to get the reporting we need, and along the way hit several issues that required significant workarounds. Sharing this feedback in case it's useful for future development.

πŸ› Critical Bug: Silent crash when BDD node lacks Gherkin model type
Using CreateNode("title") instead of CreateNode("title") produces no error at creation time, but causes a RuntimeBinderException: Cannot perform runtime binding on a null reference at Flush() when the Spark Razor template tries to dynamically access the node's Gherkin model. This was extremely hard to diagnose.

Ask: Throw an ArgumentException at CreateNode() time if a node is added to a BDD tree without a Gherkin type, or default to a sensible

3-level tree depth limit β€” Spark template is hardcoded for Feature β†’ Scenario β†’ Steps. We need Feature β†’ Platform Group β†’ Scenario β†’ Steps for parallel cross-platform runs. Flattened to 3 levels and prefix scenario titles with [Android/Appium] / [iOS/Xamarin.UITest]

No native tag rendering β€” Tags like @smoketest should display above the scenario/feature title, styled distinctly.
Inject into node titles + Config.JS DOM manipulation to reposition spans before the auto-prepended keyword

Gherkin keyword auto-prepending can't be controlled β€” CreateNode("TC001") always renders as Scenario: TC001. When injecting HTML into titles, the keyword gets prepended before the HTML. JS injection via Config.JS to move DOM nodes after render

Thread-safety gaps β€” No documented concurrency model. We use ConcurrentDictionary for feature/scenario tracking and lock around node creation. Manual thread-safety wrapper with concurrent collections

No .Description() for scenario nodes β€” Only features support descriptions. Append description as an Info log entry (loses semantic meaning)

No data table / DocString rendering β€” Gherkin tables and DocStrings from step definitions have no API support.
Format as HTML <table> / <pre> and pass via .Info()

No structured failure details β€” .Fail() accepts a string or exception but no collapsible sections for stack trace, screenshot, environment info.
HTML string with <details><summary> blocks in .Fail() message

Default BDD theme needs modernization β€” Step nodes all look the same visually regardless of pass/fail until you read the status icon.
Custom CSS via Config.CSS for status-colored backgrounds

Tag rendering workaround (JS injection)
_sparkReporter.Config.JS = @"
    document.addEventListener('DOMContentLoaded', function() {
        document.querySelectorAll('.tag-line').forEach(function(tag) {
            var parent = tag.parentNode;
            if (!parent) return;
            var firstChild = parent.firstChild;
            if (firstChild && firstChild !== tag) {
                var br = tag.nextSibling;
                if (br && br.nodeName === 'BR') {
                    parent.removeChild(br);
                }
                var newBr = document.createElement('br');
                parent.insertBefore(tag, firstChild);
                tag.after(newBr);
            }
        });
    });
";
Tag-line CSS injection
_sparkReporter.Config.CSS = @"
    .tag-line {
        display: inline;
        font-size: 12px;
        color: #1565c0;
        font-weight: 600;
        letter-spacing: 0.3px;
    }
";
Scenario title construction with tags + platform label ``` if (scenarioTags != null && scenarioTags.Length > 0) { var tagLine = string.Join(" ", scenarioTags.Select(t => t.StartsWith("@") ? t : $"@{t}")); scenarioDisplayTitle = $"{tagLine}
{platformLabel} {scenarioTitle}"; } else { scenarioDisplayTitle = $"{platformLabel} {scenarioTitle}"; } _currentTest = featureNode.CreateNode(scenarioDisplayTitle); ```
Thread-safe feature node tracking ``` private static readonly ConcurrentDictionary _featureNodes = new();

var featureNode = _featureNodes.GetOrAdd(featureTitle, title =>
{
lock (_reportLock)
{
return _extent.CreateTest(featureDisplayTitle);
}
});

</details>

πŸ“Ž Full write-up
We've documented all 10 issues with full context, workaround code, and specific asks in a detailed markdown file:

[BDD_FEEDBACK.md](https://github.com/user-attachments/files/28480279/BDD_FEEDBACK.md)

[ExtentReport.html](https://github.com/user-attachments/files/28480305/ExtentReport.html)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions