-
Notifications
You must be signed in to change notification settings - Fork 1
Integration ReqNRoll TUnit
Example project: A complete working example is available at
examples/Example.Api/tests/Example.Api.Tests.Component.ReqNRoll.TUnit/.
This guide walks you through integrating Kronikol with ReqNRoll (the successor to SpecFlow) using TUnit as the test runner. After completing this guide, your ReqNRoll BDD tests will automatically generate:
- PlantUML sequence diagrams from HTTP traffic between your service and its dependencies
- HTML reports with embedded diagrams and Gherkin steps (Given/When/Then/And/But)
- YAML specification files with Gherkin steps included
- .NET 8.0 SDK or later (recommended: .NET 10.0)
- An ASP.NET Core API project to test (your "Service Under Test")
- Basic familiarity with ReqNRoll / SpecFlow and Gherkin syntax
- Basic familiarity with TUnit
Create a new TUnit test project:
dotnet new tunit -n MyApi.Tests.Component.ReqNRollTUnitImportant: TUnit uses the Microsoft Testing Platform and requires <OutputType>Exe</OutputType>. For .NET 10+, also set <TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>:
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>MyApi.Tests.Component.ReqNRollTUnit</RootNamespace>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
</PropertyGroup>Namespace collision warning: If your project name ends in
.TUnit, you must set<RootNamespace>to a value that does not end in.TUnit(e.g.MyApi.Tests.Component.ReqNRollTUnitinstead ofMyApi.Tests.Component.ReqNRoll.TUnit). This avoids a namespace collision where Reqnroll's auto-generated code referencesTUnit.Corewithout theglobal::prefix, causing the compiler to resolve it against your project namespace instead of the TUnit framework.
Add the following packages to your test project:
dotnet add package Kronikol.ReqNRoll.TUnit
dotnet add package Reqnroll --version 3.3.4
dotnet add package Reqnroll.TUnit --version 3.3.4
dotnet add package TUnit --version 1.33.0
dotnet add package Microsoft.AspNetCore.Mvc.TestingYour <ItemGroup> should look like this:
<ItemGroup>
<PackageReference Include="Kronikol.ReqNRoll.TUnit" Version="2.31.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.5" />
<PackageReference Include="Reqnroll" Version="3.3.4" />
<PackageReference Include="Reqnroll.TUnit" Version="3.3.4" />
<PackageReference Include="TUnit" Version="1.33.0" />
</ItemGroup>Create a reqnroll.json file in your test project root. This step is mandatory — without it, the library's hooks will not be discovered and all tests will fail with InvalidOperationException: No ReqNRoll scenario is currently executing.
{
"$schema": "https://schemas.reqnroll.net/reqnroll-config-latest.json",
"bindingAssemblies": [
{
"assembly": "Kronikol.ReqNRoll.TUnit"
}
],
"formatters": {
"html": { "outputFilePath": "reqnroll_report.html" }
}
}Why is this needed? ReqNRoll only auto-discovers [Binding] classes in the test project's own assembly. The library's hooks (ReqNRollTrackingHooks, ReqNRollTestRunHooks) live in the Kronikol.ReqNRoll.TUnit assembly, so you must explicitly register it.
The formatters.html section enables ReqNRoll's native HTML report. When enabled, the library additionally enhances the report with sequence diagram images attached to each scenario's last step — on top of the standard custom reports that are always generated in the Reports directory. See the Integration ReqNRoll xUnit2 guide for full details on how the enhancement works.
Create a Hooks/TestSetupHooks.cs file. This class:
- Creates a
WebApplicationFactoryfor your API and registers diagram tracking - Provides each scenario with a tracking-enabled
HttpClient - Generates the reports after all tests complete
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Reqnroll;
using Reqnroll.BoDi;
using Kronikol;
using Kronikol.ReqNRoll.TUnit;
namespace MyApi.Tests.Component.ReqNRollTUnit.Hooks;
[Binding]
public class TestSetupHooks
{
private const string ServiceUnderTestName = "My API";
private static WebApplicationFactory<Program>? _factory;
[BeforeTestRun]
public static void BeforeTestRun()
{
_factory = new WebApplicationFactory<Program>().WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
// Register diagram tracking for all outgoing HTTP calls
services.TrackDependenciesForDiagrams(new ReqNRollTestTrackingMessageHandlerOptions
{
CallerName = ServiceUnderTestName,
// Map ports to friendly service names for diagram labels
PortsToServiceNames =
{
{ 80, ServiceUnderTestName },
{ 5001, "Downstream Service A" },
{ 5002, "Downstream Service B" }
}
});
});
});
}
[BeforeScenario]
public void BeforeScenario(IObjectContainer objectContainer)
{
// Create a tracking-enabled HttpClient and register it for injection
var client = _factory!.CreateTestTrackingClient(
new ReqNRollTestTrackingMessageHandlerOptions
{
FixedNameForReceivingService = ServiceUnderTestName
});
objectContainer.RegisterInstanceAs(client);
}
[AfterScenario]
public void AfterScenario(IObjectContainer objectContainer)
{
var client = objectContainer.Resolve<HttpClient>();
client.Dispose();
}
[AfterTestRun]
public static void AfterTestRun()
{
// Generate the HTML and YAML reports with diagrams
ReqNRollReportGenerator.CreateStandardReportsWithDiagrams(
new ReportConfigurationOptions
{
SpecificationsTitle = "My API Specifications"
});
_factory?.Dispose();
}
}| Hook | Purpose |
|---|---|
[BeforeTestRun] |
Creates the WebApplicationFactory and registers HTTP tracking |
[BeforeScenario] |
Creates a fresh tracking HttpClient for each scenario and injects it via IObjectContainer
|
[AfterScenario] |
Disposes the HttpClient
|
[AfterTestRun] |
Generates all reports (HTML + YAML) with embedded sequence diagrams |
Create a .feature file under a Features/ directory, e.g. Features/Cake.feature:
@endpoint:/cake
Feature: Cake
As a dessert provider
I want to create cakes from ingredients
So that customers can enjoy delicious cakes
@happy-path
Scenario: Calling Create Cake Endpoint Successfully
Given a valid post request for the Cake endpoint
When the request is sent to the cake post endpoint
Then the response should be successful
Scenario: Calling Create Cake Endpoint Without Eggs
Given a valid post request for the Cake endpoint
But the request body is missing eggs
When the request is sent to the cake post endpoint
Then the response http status should be bad request| Tag | Purpose |
|---|---|
@endpoint:/cake |
Sets the endpoint label on the feature in the report |
@happy-path |
Marks the scenario as a "happy path" (highlighted in reports, filterable) |
Create a StepDefinitions/CakeStepDefinitions.cs. Note that the HttpClient is constructor-injected by ReqNRoll's DI container (registered by TestSetupHooks.BeforeScenario):
using System.Net;
using System.Net.Http.Json;
using Reqnroll;
namespace MyApi.Tests.Component.ReqNRollTUnit.StepDefinitions;
[Binding]
public class CakeStepDefinitions
{
private readonly HttpClient _client;
private HttpResponseMessage? _response;
public CakeStepDefinitions(HttpClient client)
{
_client = client;
}
[Given("a valid post request for the Cake endpoint")]
public async Task GivenAValidPostRequestForTheCakeEndpoint()
{
// Set up your request data
}
[Given("the request body is missing eggs")]
public void GivenTheRequestBodyIsMissingEggs()
{
// Modify request to remove eggs
}
[When("the request is sent to the cake post endpoint")]
public async Task WhenTheRequestIsSentToTheCakePostEndpoint()
{
_response = await _client.PostAsJsonAsync("cake", /* your request */);
}
[Then("the response should be successful")]
public async Task ThenTheResponseShouldBeSuccessful()
{
_response!.StatusCode.Should().Be(HttpStatusCode.OK);
}
[Then("the response http status should be bad request")]
public void ThenTheResponseHttpStatusShouldBeBadRequest()
{
_response!.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
}TUnit uses the Microsoft Testing Platform, so tests can be run with:
dotnet run --project MyApi.Tests.Component.ReqNRollTUnitOr using dotnet test (with TestingPlatformDotnetTestSupport enabled):
dotnet testAfter the tests complete, check the bin/Debug/net10.0/Reports/ folder. You should find three files:
| File | Description |
|---|---|
Specifications.html |
HTML specifications with embedded PlantUML sequence diagrams |
TestRunReport.html |
HTML test run report with diagrams and execution summary |
Specifications.yml |
YAML specifications with Gherkin steps |
The ReqNRoll adapter produces richer reports than the XUnit/NUnit adapters because it has access to Gherkin metadata:
- Gherkin steps — Each scenario in the HTML report shows its Given/When/Then/And/But steps with keyword highlighting
-
Feature descriptions — The feature description from the
.featurefile is shown under the feature heading -
YAML with steps — The YAML spec includes a
Steps:array per scenario with the full Gherkin steps -
Automatic attachment capture — The
AttachmentCapturingPlugin(auto-registered via[assembly: RuntimePlugin]) decoratesIReqnrollOutputHelperso that any call toAddAttachment()in step definitions is automatically forwarded toTrack.Attachment(). Attachments then appear as downloadable links in the report alongside the step that produced them, with no additional wiring required. See Step Tracking#File Attachments.
Pass these when calling ReqNRollReportGenerator.CreateStandardReportsWithDiagrams:
| Property | Default | Description |
|---|---|---|
SpecificationsTitle |
"Service Specifications" |
Title shown at the top of reports |
PlantUmlServerBaseUrl |
"https://plantuml.com/plantuml" |
PlantUML server URL for diagram rendering |
HtmlSpecificationsFileName |
"Specifications" |
Output filename for the specifications HTML |
HtmlTestRunReportFileName |
"TestRunReport" |
Output filename for the test run HTML |
YamlSpecificationsFileName |
"Specifications" |
Output filename for the YAML specs |
HtmlSpecificationsCustomStyleSheet |
Stylesheets.VioletThemeStyleSheet |
Custom CSS to append to the specifications HTML |
ExcludedHeaders |
[] |
HTTP headers to exclude from diagrams |
RequestResponsePostProcessor |
null |
Post-processing function for request/response content in diagrams |
SeparateSetup |
false |
When true, HTTP calls made during GIVEN steps are wrapped in a visual "Setup" partition in the diagram |
HighlightSetup |
true |
When true (and SeparateSetup is enabled), the setup partition is rendered with a background colour |
Pass these when calling TrackDependenciesForDiagrams and CreateTestTrackingClient:
| Property | Description |
|---|---|
CallerName |
Display name for the service making outgoing HTTP calls |
FixedNameForReceivingService |
Display name for the service receiving requests (your SUT) |
PortsToServiceNames |
Dictionary mapping port numbers to friendly service names. Unmapped ports appear as localhost_80, localhost_5001, etc. |
Setup separation: When
SeparateSetup = true, ReqNRoll automatically detects the boundary between GIVEN steps and WHEN/THEN steps. No manualStartAction()call is needed.
| Aspect | xUnit3 | TUnit |
|---|---|---|
| Test runner | xUnit v3 (out-of-process) | TUnit (Microsoft Testing Platform) |
| Execution | dotnet test |
dotnet run or dotnet test (with TestingPlatformDotnetTestSupport) |
| NuGet package | Reqnroll.xunit.v3 |
Reqnroll.TUnit |
| Adapter package | Kronikol.ReqNRoll.xUnit3 |
Kronikol.ReqNRoll.TUnit |
| Binding assembly | Kronikol.ReqNRoll.xUnit3 |
Kronikol.ReqNRoll.TUnit |
| Namespace collision | None | Must set <RootNamespace> to avoid .TUnit suffix collision |
Use @category: tags to group scenarios into named categories. Categories appear in generated reports and can be used for filtering:
@category:payments
@category:critical
Feature: Payment ProcessingMultiple @category: tags can be applied to a single feature or scenario. The prefix is case-insensitive.
Gherkin Rule blocks are fully supported. The rule title is captured and included in the generated report data:
Feature: Cake Ordering
Rule: Cakes must contain at least one ingredient
Scenario: Order cake with ingredients
Given a cake with flour and sugar
When I place the order
Then the order succeedsScenario Outlines with Examples tables are tracked automatically. Each example row produces a separate scenario in the report, with the example values captured:
Scenario Outline: Creating a <flavour> cake
Given a request for a <flavour> cake with <layers> layers
When the request is sent
Then the response contains a <flavour> cake
Examples:
| flavour | layers |
| chocolate | 3 |
| vanilla | 2 |The OutlineId (the scenario title template) and ExampleValues dictionary (column→value) are captured and available in the report data.
Data tables attached to steps are captured as TableText and included in the report:
Scenario: Create cake with specific ingredients
Given the following ingredients:
| Ingredient | Amount |
| Flour | 200g |
| Sugar | 100g |
When the cake is baked
Then it should be deliciousMulti-line DocStrings on steps are captured and available in the report data:
Scenario: Create cake from recipe
Given the following recipe:
"""json
{
"name": "Chocolate Cake",
"ingredients": ["flour", "cocoa", "sugar"]
}
"""
When the cake is baked
Then it should match the recipeThe DocStringMediaType field exists on the ScenarioStep record but is not currently populated automatically by the ReqNRoll adapter. It will always be null.
You are missing the reqnroll.json file, or it does not list Kronikol.ReqNRoll.TUnit in bindingAssemblies. See Step 3.
Your project namespace ends in .TUnit, which collides with TUnit.Core references in Reqnroll's generated code. Set <RootNamespace> to a value that doesn't end in .TUnit. See Step 1.
- Ensure
[AfterTestRun]callsReqNRollReportGenerator.CreateStandardReportsWithDiagrams - If any test has failed, the specifications HTML and YAML files will be blank (by design — they only generate on fully passing runs). The
TestRunReport.htmlwill still be generated regardless.
Ensure you are using Kronikol.ReqNRoll.TUnit version 2.0.78-beta or later. Earlier versions used StepDefinitionType (which collapses keywords) instead of StepDefinitionKeyword.
Getting Started
Common Tasks
Integration Guides
- Integration xUnit3
- Integration xUnit2
- Integration NUnit
- Integration MSTest
- Integration TUnit
- Integration BDDfy xUnit3
- Integration LightBDD xUnit2
- Integration LightBDD xUnit3
- Integration LightBDD TUnit
- Integration ReqNRoll xUnit2
- Integration ReqNRoll xUnit3
- Integration ReqNRoll TUnit
Extensions
- Integration AtlasDataApi Extension
- Integration BigQuery Extension
- Integration Bigtable Extension
- Integration BlobStorage Extension
- Integration ClickHouse Extension
- Integration CloudStorage Extension
- Integration CosmosDB Extension
- Integration Dapper Extension
- Integration DynamoDB Extension
- Integration EF Core Relational Extension
- Integration Elasticsearch Extension
- Integration EventBridge Extension
- Integration EventHubs Extension
- Integration Grpc Extension
- Integration Kafka Extension
- Integration MassTransit Extension
- Integration MongoDB Extension
- Integration MySqlConnector Extension
- Integration Npgsql Extension
- Integration Oracle Extension
- Integration PubSub Extension
- Integration Redis Extension
- Integration S3 Extension
- Integration ServiceBus Extension
- Integration SNS Extension
- Integration Spanner Extension
- Integration SqlClient Extension
- Integration Sqlite Extension
- Integration SQS Extension
- Integration StorageQueues Extension
- Integration OpenTelemetry Extension
- Integration DispatchProxy Extension
- Integration MediatR Extension
- Integration PlantUML IKVM
Configuration
- Tracking Dependencies
- Tracking Custom Dependencies
- HTTP Tracking Setup
- Report Configuration
- Diagram Customisation
- Phase-Aware Tracking
- Content Formatting
- PlantUML Server Configuration
Features
- Generated Reports
- Search Syntax
- Component Diagrams
- PlantUML Browser Rendering
- Inline SVG Rendering
- Internal Flow Tracking
- Tags and Attributes
- Excluding Requests
- Excluded Headers
- Multi-Host Test Architectures
- Event-Driven Architecture Testing
- Service Bus Tracking Patterns
- Background Thread Correlation
- Parallel-Safe Background Correlation
- Event & Message Tracking
- Assertion Tracking
- Step Tracking
- Tabular Attributes
- Large Response and Diagram Handling
- Diagnostics and Debugging
- CI Summary Integration
- CI Artifact Upload
- Merging Parallel Reports
Reference