SoftwareTestPilot
Automation TestingPublished: Updated: · 5 days ago26 min read

SpecFlow C# Test Automation Guide (2026)

The 2026 SpecFlow C# tutorial — set up SpecFlow + .NET 8 + NUnit + Selenium, write production Gherkin feature files, C# step definitions, table transformations, hooks, LivingDoc and CI/CD.

Avinash Kamble
Avinash Kamble
Founder & QA Engineer at SoftwareTestPilot
Reviewed by Priyanka G.
Share:XLinkedInWhatsApp
SpecFlow C# Test Automation Guide — 3D book cover with SpecFlow, C# and .NET logos
SpecFlow C# Test Automation Guide — 3D book cover with SpecFlow, C# and .NET logos
In this article
  1. 1. What Is SpecFlow?
  2. 2. SpecFlow vs Cucumber
  3. 3. Install SpecFlow Step by Step
  4. 4. Your First Feature File
  5. 5. Step Definitions in C#
  6. 6. Scenario Outlines and Examples
  7. 7. Data Tables and Table Transformations
  8. 8. Hooks and Step Argument Transformations
  9. 9. SpecFlow + Selenium Integration
  10. 10. Living Documentation with SpecFlow+
  11. 11. CI/CD Integration Patterns
  12. 12. SpecFlow Best Practices
  13. 13. Author Bio & Next Steps
  14. Frequently asked questions

What you'll master: a working SpecFlow + .NET 8 + NUnit + Selenium setup, production-quality Gherkin feature files bound to C# step definitions, table transformations for clean data binding, hooks for setup/teardown, and a CI/CD-ready pipeline.

1. What Is SpecFlow?

SpecFlow is the .NET port of Cucumber — it brings Behavior-Driven Development (BDD) to the Microsoft ecosystem: C#, .NET, Visual Studio and Azure DevOps. After Tricentis acquired SpecFlow in 2020 the project has continued to ship, and SpecFlow 3.9+ supports .NET 6, 7, 8 and 9.

SpecFlow remains the default BDD choice for .NET shops because of:

  • Native .NET integration with NUnit, xUnit and MSTest
  • Visual Studio + ReSharper / Rider first-class IDE experience with Gherkin IntelliSense and live step linking
  • Living documentation via SpecFlow+ LivingDoc
  • Azure DevOps, MSBuild and TFS integration out of the box

For broader BDD context, pair this guide with our BDD Cucumber tutorial and the dedicated SpecFlow interview questions hub.

🚀 Skip the boilerplate. Rehearse SpecFlow rounds live with our AI mock interview and review Selenium interview questions alongside the BDD prep.

2. SpecFlow vs Cucumber

DimensionSpecFlowCucumber-JVM
LanguageC# / VB.NETJava
Test runnerNUnit / xUnit / MSTestJUnit / TestNG
IDE supportVisual Studio + ReSharperIntelliJ + Eclipse
Living docsSpecFlow+ LivingDocClueCumber, Cucumber Reports
EcosystemMicrosoft / Azure DevOpsJava / Maven / Gradle
Best for.NET shopsJava shops

Both frameworks use the same Gherkin syntax, so scenarios port between them with minimal change. For a Java + Cucumber alternative, see our Java for Selenium Automation guide.

3. Install SpecFlow Step by Step

Prerequisites

  • .NET 8 SDK (recommended) or .NET 9 SDK
  • Visual Studio 2022 or JetBrains Rider
  • SpecFlow extension for Visual Studio (free, from the Marketplace)

Step 1 — Create a project

dotnet new nunit -n SpecFlowDemo
cd SpecFlowDemo

Step 2 — Add SpecFlow packages

dotnet add package SpecFlow
dotnet add package SpecFlow.NUnit
dotnet add package SpecFlow.Tools.MsBuild.Generation

# Selenium integration
dotnet add package Selenium.WebDriver
dotnet add package Selenium.Support

Step 3 — Configure SpecFlow in .csproj

<ItemGroup>
  <PackageReference Include="SpecFlow" Version="3.9.7" />
  <PackageReference Include="SpecFlow.NUnit" Version="3.9.7" />
  <PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.9.7">
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
</ItemGroup>

Step 4 — Verify the install

dotnet build
dotnet test

Project structure

SpecFlowDemo/
├── Features/
│   └── Login.feature
├── StepDefinitions/
│   └── LoginSteps.cs
├── Pages/
│   ├── BasePage.cs
│   └── LoginPage.cs
├── Hooks/
│   └── ScenarioHooks.cs
├── Drivers/
│   └── DriverFactory.cs
└── SpecFlowDemo.csproj

4. Your First Feature File

Create Features/Login.feature:

Feature: User login
  As a registered user
  I want to log in to the application
  So that I can access my account

Background:
  Given the application is running

Scenario: Successful login with valid credentials
  Given I am on the login page
  When I enter "admin@example.com" as the email
  And I enter "Sup3rSecret!" as the password
  And I click the "Sign in" button
  Then I should be on the dashboard page
  And I should see "Welcome, admin"

Scenario: Failed login with invalid password
  Given I am on the login page
  When I enter "admin@example.com" as the email
  And I enter "wrong-password" as the password
  And I click the "Sign in" button
  Then I should see an error message "Invalid credentials"

In Visual Studio, right-click the .feature file → Generate Step Definitions. SpecFlow scaffolds a stub class for every step.

5. Step Definitions in C#

Create StepDefinitions/LoginSteps.cs:

using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using SeleniumExtras.WaitHelpers;
using TechTalk.SpecFlow;
using SpecFlowDemo.Pages;

namespace SpecFlowDemo.StepDefinitions
{
    [Binding]
    public class LoginSteps
    {
        private readonly ScenarioContext _scenarioContext;
        private readonly IWebDriver _driver;
        private readonly LoginPage _loginPage;

        public LoginSteps(ScenarioContext scenarioContext)
        {
            _scenarioContext = scenarioContext;
            _driver = (IWebDriver)_scenarioContext["driver"];
            _loginPage = new LoginPage(_driver);
        }

        [Given(@"I am on the login page")]
        public void GivenIAmOnTheLoginPage() => _loginPage.Navigate();

        [When(@"I enter ""([^""]*)"" as the email")]
        public void WhenIEnterEmail(string email) => _loginPage.EnterEmail(email);

        [When(@"I enter ""([^""]*)"" as the password")]
        public void WhenIEnterPassword(string password) => _loginPage.EnterPassword(password);

        [When(@"I click the ""([^""]*)"" button")]
        public void WhenIClickButton(string label) =>
            _driver.FindElement(By.CssSelector("button[type='submit']")).Click();

        [Then(@"I should be on the dashboard page")]
        public void ThenIShouldBeOnTheDashboardPage()
        {
            var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));
            wait.Until(ExpectedConditions.UrlContains("/dashboard"));
            Assert.That(_driver.Url, Does.Contain("/dashboard"));
        }

        [Then(@"I should see ""([^""]*)""")]
        public void ThenIShouldSee(string text) =>
            Assert.That(_driver.PageSource, Does.Contain(text));
    }
}

The [Binding] attribute tells SpecFlow this class contains step definitions. Each method has a [Given], [When] or [Then] attribute with a regex that matches the Gherkin step.

6. Scenario Outlines and Examples

Run the same scenario against multiple datasets:

Scenario Outline: Login with multiple users
  Given I am on the login page
  When I enter "<email>" as the email
  And I enter "<password>" as the password
  And I click the "Sign in" button
  Then I should see "<greeting>"

Examples:
  | email              | password     | greeting        |
  | admin@example.com  | Sup3rSecret! | Welcome, admin  |
  | viewer@example.com | ViewerPass1! | Welcome, viewer |
  | guest@example.com  | GuestPass1!  | Welcome, guest  |

SpecFlow runs this scenario once per row. Step definitions are identical to a regular scenario — SpecFlow substitutes the placeholders automatically.

7. Data Tables and Table Transformations

Basic DataTable usage

Scenario: Apply multiple promo codes
  Given I am on the cart page
  When I apply the following promo codes:
    | code      | expected_discount |
    | WELCOME10 | 10%               |
    | SAVE20    | 20%               |
    | VIP30     | 30%               |
  Then I should see the total discount applied
[When(@"I apply the following promo codes:")]
public void WhenIApplyPromoCodes(Table table)
{
    foreach (var row in table.Rows)
    {
        var code = row["code"];
        var expectedDiscount = row["expected_discount"];
        // Apply code, verify expected_discount
    }
}

Table transformations (the killer feature)

public class PromoCode
{
    public string Code { get; set; }
    public string ExpectedDiscount { get; set; }
}

[Binding]
public class PromoCodeTransforms
{
    [StepArgumentTransformation]
    public IEnumerable<PromoCode> TransformPromoCodes(Table table) =>
        table.CreateSet<PromoCode>();
}

[When(@"I apply the following promo codes:")]
public void WhenIApplyPromoCodes(IEnumerable<PromoCode> promoCodes)
{
    foreach (var promo in promoCodes)
    {
        // Apply promo.Code, verify promo.ExpectedDiscount
    }
}

This eliminates the foreach (var row in table.Rows) boilerplate and gives you strongly-typed access.

Vertical data tables

Scenario: Verify user profile fields
  Given I am on the user profile page
  Then the user profile should show:
    | field      | value         |
    | name       | Alice Johnson |
    | email      | alice@ex.com  |
    | role       | admin         |
    | department | Engineering   |

8. Hooks and Step Argument Transformations

Hooks

[Binding]
public class ScenarioHooks
{
    private readonly ScenarioContext _scenarioContext;

    public ScenarioHooks(ScenarioContext scenarioContext)
        => _scenarioContext = scenarioContext;

    [BeforeScenario]
    public void BeforeScenario() =>
        _scenarioContext["driver"] = new ChromeDriver();

    [BeforeScenario("@database")]
    public void BeforeDatabaseScenario() => DatabaseHelper.ResetDatabase();

    [AfterScenario]
    public void AfterScenario()
    {
        var driver = (IWebDriver)_scenarioContext["driver"];
        driver?.Quit();
    }

    [BeforeTestRun]
    public static void BeforeTestRun() { /* once before all tests */ }

    [AfterTestRun]
    public static void AfterTestRun() { /* once after all tests */ }
}

Step argument transformations

[Binding]
public class Transforms
{
    [StepArgumentTransformation(@"(\d+) days? ago")]
    public DateTime DaysAgoTransform(int days) =>
        DateTime.UtcNow.AddDays(-days);

    [StepArgumentTransformation(@"user ""([^""]+)""")]
    public User UserTransform(string email) =>
        UserRepository.FindByEmail(email);
}

Now your Gherkin can say:

Given the user "admin@example.com" registered 30 days ago

And the step definition accepts User and DateTime directly — no manual parsing.

9. SpecFlow + Selenium Integration

Driver factory

namespace SpecFlowDemo.Drivers
{
    public static class DriverFactory
    {
        public static IWebDriver CreateDriver(string browser) => browser.ToLower() switch
        {
            "chrome"  => new ChromeDriver(),
            "firefox" => new FirefoxDriver(),
            "edge"    => new EdgeDriver(),
            _         => throw new ArgumentException($"Unknown browser: {browser}")
        };
    }
}

Page object (POM adapted for SpecFlow)

namespace SpecFlowDemo.Pages
{
    public class LoginPage
    {
        private readonly IWebDriver _driver;
        public LoginPage(IWebDriver driver) => _driver = driver;

        public void Navigate() =>
            _driver.Navigate().GoToUrl("https://example.com/login");

        public void EnterEmail(string email) =>
            _driver.FindElement(By.Id("email")).SendKeys(email);

        public void EnterPassword(string password) =>
            _driver.FindElement(By.Id("password")).SendKeys(password);

        public void ClickSignIn() =>
            _driver.FindElement(By.Id("submit")).Click();
    }
}

For the full Selenium POM pattern, see our Selenium WebDriver guide and the Java for Selenium deep-dive.

10. Living Documentation with SpecFlow+

SpecFlow+ LivingDoc generates human-readable HTML documentation from your feature files — the BDD payoff. Scenarios become the executable specification of your system.

Setup

dotnet add package SpecFlow.Plus.LivingDocPlugin
dotnet add package SpecFlow.Plus.LivingDoc

Generate the docs

livingdoc test-assembly SpecFlowDemo.dll -t TestExecution.json

You get an HTML site with a navigable tree of feature files, scenario results, pass/fail trends, execution times and tag filters.

Publish to Azure DevOps / GitHub Pages

# Azure DevOps
- task: CopyFiles@2
  inputs:
    SourceFolder: '$(Build.ArtifactStagingDirectory)/livingdoc'
    TargetFolder: '$(Build.ArtifactStagingDirectory)/drop'

- task: PublishBuildArtifacts@1
  inputs:
    PathToPublish: '$(Build.ArtifactStagingDirectory)/drop'

11. CI/CD Integration Patterns

GitHub Actions

name: SpecFlow Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'
      - run: dotnet restore
      - run: dotnet build --no-restore
      - run: dotnet test --no-build --logger "trx;LogFileName=test-results.trx"
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: test-results
          path: TestResults/

Azure DevOps

trigger:
  - main

pool:
  vmImage: 'windows-latest'

steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'test'
    arguments: '--logger trx --collect:"XPlat Code Coverage"'
- task: PublishTestResults@2
  inputs:
    testRunner: 'VSTest'
    testResultsFiles: '**/*.trx'

Parallel execution

SpecFlow runs in parallel via NUnit's Parallelizable attribute:

[assembly: Parallelizable(ParallelScope.Fixtures)]

Each feature file runs in parallel — use carefully, parallel features can't share state.

12. SpecFlow Best Practices

Do

  • Use Visual Studio + ReSharper (or Rider) for the best BDD experience
  • Generate LivingDoc for every release
  • Use Page Object Model for clean step definitions
  • Use table transformations for typed data binding
  • Use Scenario Outlines for data-driven scenarios
  • Use tags to organize by feature, priority and test type
  • Use ScenarioContext sparingly — only for driver and minimal cross-step state
  • Pair SpecFlow with NUnit for the richest test runner features

Don't

  • Don't put business logic in step definitions — they should be thin glue
  • Don't write imperative scenarios — use declarative language
  • Don't use ScenarioContext.Current (deprecated in SpecFlow 3) — use constructor injection
  • Don't share driver instances across scenarios without cleanup
  • Don't run scenarios in parallel without isolation
  • Don't skip the discovery workshop — BDD without collaboration is keyword-driven testing

13. Author Bio & Next Steps

About the author: The SoftwareTestPilot Editorial Team is a group of QA practitioners with 40+ years of combined experience. We've shipped SpecFlow suites in finance, healthcare and SaaS — and we know what works and what wastes time.

Continue Your SpecFlow Journey

Frequently asked questions

What is SpecFlow?

SpecFlow is the .NET port of Cucumber. It lets .NET developers write Gherkin feature files and bind them to C# step definitions — the most popular BDD framework for the Microsoft ecosystem.

SpecFlow vs Cucumber — which should I use?

Use SpecFlow if you're in a .NET shop. Use Cucumber if you're in a Java/JS/Python shop. Both use the same Gherkin syntax, so scenarios port between them with minimal effort.

Is SpecFlow free?

Yes — SpecFlow 3.x is free and open source. Tricentis offers commercial add-ons like SpecFlow+ LivingDoc and SpecFlow+ Excel, but the core framework is free.

Does SpecFlow work with .NET 8 and .NET 9?

Yes — SpecFlow 3.9+ supports .NET 6, 7, 8 and 9. Use the latest version for the best .NET 8/9 experience.

What's the difference between SpecFlow and Reqnroll?

Reqnroll is a community fork of SpecFlow that emerged in 2024 after some uncertainty about SpecFlow's roadmap. For greenfield projects, evaluate both. For existing SpecFlow projects, stay on SpecFlow unless there's a specific reason to migrate.

How do I generate step definition stubs?

In Visual Studio, right-click the .feature file → Generate Step Definitions. SpecFlow creates a stub class with all the steps as methods to implement.

What is SpecFlow+ LivingDoc?

A tool that generates human-readable HTML documentation from your feature files. It's the BDD payoff — scenarios become the executable specification of your system.

Can SpecFlow run tests in parallel?

Yes — via NUnit's [Parallelizable] attribute. Each feature file runs in parallel; be careful with shared state across scenarios.

How long does it take to learn SpecFlow?

For an experienced C# developer: 1–2 weeks to productive. For a developer new to BDD: 4–6 weeks including the Three Amigos workshop process.

Is SpecFlow still relevant?

Yes — SpecFlow 3.9+ is actively maintained and widely deployed in enterprise .NET shops. It remains the default BDD choice for .NET teams.

Keep going

Practice these questions

Rehearse Selenium and Playwright automation questions covering framework design, waits, locators and CI/CD.

Found this useful?
Share:XLinkedInWhatsApp

Was this article helpful?

Keep building your QA edge

Continue reading

Join the QA Community

Connect with fellow testers, share job leads, and get career advice.

Premium QA Resources

Stop Reinventing the Wheel. Upgrade Your QA Arsenal.

Take your testing skills from beginner to Lead Engineer. Supercharge your daily workflow with our premium digital resources.

  • ⚡ Ready-to-use testing strategy templates
  • 🔥 Advanced API & UI automation guides
  • ⏱️ Save 10+ hours a week on test planning
4.9/5 rating
Explore All Products

⭐⭐⭐⭐⭐ Trusted by 1,000+ Software Test Pilots • Instant Access