Skip to main content

C# BDD Testing with Reqnroll

Reqnroll is an open-source .NET test automation framework for Behavior-Driven Development (BDD). It is the community-maintained successor to SpecFlow and is fully compatible with Gherkin syntax.

Reqnroll works with all major .NET versions (.NET 6.0, 7.0, 8.0, 9.0) and integrates with popular test frameworks:

  • NUnit
  • xUnit
  • MSTest
  • TUnit

Migrating from SpecFlow? Reqnroll is based on the SpecFlow codebase and provides an easy migration path. See the migration section below.

Installation

First, install the Reqnroll for Visual Studio 2022 extension from the Visual Studio Marketplace to get syntax highlighting, navigation and project templates.

Create a new project

Create a new test project and add the required packages:

# Create a new NUnit test project
dotnet new nunit -n ReqnrollTests
cd ReqnrollTests

# Add Reqnroll with NUnit integration
dotnet add package Reqnroll.NUnit

# Add Selenium WebDriver packages
dotnet add package Selenium.WebDriver
dotnet add package Selenium.Support

Or add these packages to your .csproj file:

<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
  <PackageReference Include="NUnit" Version="4.1.0" />
  <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
  <PackageReference Include="Reqnroll.NUnit" Version="2.3.0" />
  <PackageReference Include="Selenium.WebDriver" Version="4.27.0" />
  <PackageReference Include="Selenium.Support" Version="4.27.0" />
</ItemGroup>

Alternative packages for other test frameworks:

  • Reqnroll.xUnit - for xUnit 2.x
  • Reqnroll.xUnit.v3 - for xUnit 3.x
  • Reqnroll.MsTest - for MSTest
  • Reqnroll.TUnit - for TUnit

Your first Reqnroll test

Reqnroll tests consist of three parts: a Feature file (Gherkin), Step Definitions (C#), and a Driver helper class.

1. Feature File

Create a Features folder and add Search.feature:

Feature: Google Search

Scenario: Search for TestingBot
  Given I am on the Google homepage
  When I search for "TestingBot"
  Then the page title should contain "TestingBot"

Scenario Outline: Search on multiple browsers
  Given I am using <browser> on <platform>
  And I am on the Google homepage
  When I search for "TestingBot"
  Then the page title should contain "TestingBot"

  Examples:
    | browser | platform |
    | chrome  | WIN10    |
    | firefox | WIN10    |
    | safari  | TAHOE   |

2. Driver Helper Class

Create a helper class to manage the WebDriver connection to TestingBot:

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Safari;
using OpenQA.Selenium.Remote;

namespace ReqnrollTests.Support;

public class TestingBotDriver : IDisposable
{
    public IWebDriver Driver { get; private set; }
    private bool _testPassed = true;

    public void Initialize(string browser = "chrome", string version = "latest", string platform = "WIN10")
    {
        DriverOptions options = browser.ToLower() switch
        {
            "firefox" => new FirefoxOptions(),
            "safari" => new SafariOptions(),
            _ => new ChromeOptions()
        };

        options.BrowserVersion = version;
        options.PlatformName = platform;
        options.AddAdditionalOption("tb:options", new Dictionary<string, object>
        {
            ["key"] = Environment.GetEnvironmentVariable("TB_KEY") ?? "",
            ["secret"] = Environment.GetEnvironmentVariable("TB_SECRET") ?? "",
            ["name"] = "Reqnroll Test"
        });

        Driver = new RemoteWebDriver(
            new Uri("https://hub.testingbot.com/wd/hub"),
            options
        );
    }

    public void MarkTestFailed() => _testPassed = false;

    public void Dispose()
    {
        if (Driver != null)
        {
            var status = _testPassed ? "passed" : "failed";
            ((IJavaScriptExecutor)Driver).ExecuteScript($"tb:test-result={status}");
            Driver.Dispose();
        }
    }
}

3. Step Definitions

Create StepDefinitions/SearchSteps.cs:

using OpenQA.Selenium;
using Reqnroll;
using ReqnrollTests.Support;

namespace ReqnrollTests.StepDefinitions;

[Binding]
public class SearchSteps
{
    private readonly TestingBotDriver _tbDriver;

    public SearchSteps(TestingBotDriver tbDriver)
    {
        _tbDriver = tbDriver;
    }

    [Given("I am on the Google homepage")]
    public void GivenIAmOnTheGoogleHomepage()
    {
        if (_tbDriver.Driver == null)
            _tbDriver.Initialize();

        _tbDriver.Driver.Navigate().GoToUrl("https://www.google.com");
    }

    [Given("I am using (.*) on (.*)")]
    public void GivenIAmUsingBrowserOnPlatform(string browser, string platform)
    {
        _tbDriver.Initialize(browser, "latest", platform);
    }

    [When("I search for {string}")]
    public void WhenISearchFor(string searchTerm)
    {
        var searchBox = _tbDriver.Driver.FindElement(By.Name("q"));
        searchBox.SendKeys(searchTerm);
        searchBox.Submit();
    }

    [Then("the page title should contain {string}")]
    public void ThenThePageTitleShouldContain(string expectedText)
    {
        // Wait for results to load
        Thread.Sleep(2000);
        Assert.That(_tbDriver.Driver.Title, Does.Contain(expectedText));
    }
}

4. Hooks for Setup/Teardown

Create Hooks/TestHooks.cs to manage driver lifecycle:

using Reqnroll;
using ReqnrollTests.Support;

namespace ReqnrollTests.Hooks;

[Binding]
public class TestHooks
{
    private readonly TestingBotDriver _tbDriver;

    public TestHooks(TestingBotDriver tbDriver)
    {
        _tbDriver = tbDriver;
    }

    [AfterScenario]
    public void AfterScenario(ScenarioContext scenarioContext)
    {
        if (scenarioContext.TestError != null)
        {
            _tbDriver.MarkTestFailed();
        }
        _tbDriver.Dispose();
    }
}

Run your tests with:

TB_KEY=your_key TB_SECRET=your_secret dotnet test

Specify Browsers & Devices

Configure browsers and platforms using Selenium 4's browser-specific options classes:

  • ChromeOptions for Chrome
  • FirefoxOptions for Firefox
  • EdgeOptions for Microsoft Edge
  • SafariOptions for Safari

Example using ChromeOptions:

var options = new ChromeOptions();
options.BrowserVersion = "latest";
options.PlatformName = "WIN10";
options.AddAdditionalOption("tb:options", new Dictionary<string, object>
{
    ["key"] = "YOUR_TB_KEY",
    ["secret"] = "YOUR_TB_SECRET",
    ["name"] = "My Reqnroll Test"
});

var driver = new RemoteWebDriver(
    new Uri("https://hub.testingbot.com/wd/hub"),
    options
);

To see how to configure different browsers and platforms, use the picker below:

Testing Internal Websites

Use TestingBot Tunnel to securely test internal or staged websites.

1. Download the tunnel and start it:

java -jar testingbot-tunnel.jar key secret

2. Update your driver initialization to connect through the tunnel:

// Connect through the tunnel instead of directly to TestingBot
Driver = new RemoteWebDriver(
    new Uri("http://localhost:4445/wd/hub"),
    options
);

// Now you can access internal URLs in your tests
Driver.Navigate().GoToUrl("http://internal.yourcompany.com");

Run tests in Parallel

Reqnroll supports parallel test execution when using NUnit or xUnit as the test runner.

NUnit Parallel Execution

Add to your AssemblyInfo.cs or any file:

using NUnit.Framework;

[assembly: Parallelizable(ParallelScope.Fixtures)]
[assembly: LevelOfParallelism(5)]

Configure in reqnroll.json:

{
  "$schema": "https://schemas.reqnroll.net/reqnroll-config-latest.json",
  "bindingCulture": {
    "name": "en-US"
  }
}

xUnit Parallel Execution

Add xunit.runner.json:

{
  "parallelizeAssembly": true,
  "parallelizeTestCollections": true,
  "maxParallelThreads": 5
}

Queuing

Every TestingBot plan has a limit on parallel tests. If you exceed this limit, additional tests are queued (up to 6 minutes) and run as slots become available.

Mark tests as passed/failed

Report test results to TestingBot using JavaScript execution in your [AfterScenario] hook:

[Binding]
public class TestHooks
{
    private readonly TestingBotDriver _tbDriver;

    public TestHooks(TestingBotDriver tbDriver)
    {
        _tbDriver = tbDriver;
    }

    [AfterScenario]
    public void AfterScenario(ScenarioContext scenarioContext)
    {
        if (_tbDriver.Driver != null)
        {
            var status = scenarioContext.TestError == null ? "passed" : "failed";
            ((IJavaScriptExecutor)_tbDriver.Driver).ExecuteScript($"tb:test-result={status}");
            _tbDriver.Driver.Dispose();
        }
    }
}

Migrating from SpecFlow

Reqnroll is designed as a drop-in replacement for SpecFlow. Most projects can migrate with minimal changes:

1. Update NuGet Packages

# Remove SpecFlow packages
dotnet remove package SpecFlow
dotnet remove package SpecFlow.NUnit

# Add Reqnroll packages
dotnet add package Reqnroll.NUnit

2. Update Namespaces

Replace TechTalk.SpecFlow with Reqnroll in your code:

// Before (SpecFlow)
using TechTalk.SpecFlow;

// After (Reqnroll)
using Reqnroll;

3. Update Configuration

Rename specflow.json to reqnroll.json and update the schema reference:

{
  "$schema": "https://schemas.reqnroll.net/reqnroll-config-latest.json"
}

4. Update Visual Studio Extension

Install the "Reqnroll for Visual Studio 2022" extension and uninstall the SpecFlow extension.

For a complete migration guide, see the official SpecFlow to Reqnroll migration guide.

Other C# Framework examples

  • NUnit

    An unit testing framework that is open source written in C#.

  • xUnit

    xUnit.net is a modern, extensible testing framework for .NET, widely used in the .NET ecosystem.

  • MSTest

    MSTest framework is a test framework which is included, by default, with Microsoft Visual Studio.

  • SpecFlow

    SpecFlow (deprecated) - the predecessor to Reqnroll for BDD testing with Gherkin syntax.

Was this page helpful?

Looking for More Help?

Have questions or need more information?
You can reach us via the following channels: