---
title: xUnit.net C# Automated Selenium Testing Framework
description: xUnit.net C# automated testing with Selenium. Run your tests on 6100+
  real browsers and mobile devices with TestingBot.
source_url:
  html: https://testingbot.com/support/web-automate/selenium/csharp/xunit
  md: https://testingbot.com/support/web-automate/selenium/csharp/xunit/index.md
---
# C# Automated Testing with xUnit.net

[xUnit.net](https://xunit.net/) is a free, open-source, community-focused unit testing framework for .NET. It is the successor to NUnit and was written by the original inventor of NUnit v2.

xUnit.net is widely used in the .NET ecosystem and offers modern features like:

- Built-in support for parallel test execution
- Theory tests with `[InlineData]` for data-driven testing
- Constructor/Dispose pattern for test setup and teardown
- Fixtures for sharing context between tests

## Installation

Create a new xUnit test project and add the required packages:

    # Create a new xUnit test project
    dotnet new xunit -n SeleniumTests
    cd SeleniumTests
    
    # 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="xunit" Version="2.7.0" />
      <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7" />
      <PackageReference Include="Selenium.WebDriver" Version="4.18.1" />
      <PackageReference Include="Selenium.Support" Version="4.18.1" />
    </ItemGroup>

**Requirements:** xUnit 2.x requires .NET 6.0 or higher. The examples use modern C# features like nullable reference types.

## Your first xUnit test

The example below shows a complete xUnit test that connects to TestingBot and runs a simple browser test.   
 xUnit uses constructor/destructor (or `IDisposable`) for setup and teardown instead of attributes.

**GoogleTest.cs** : 

    using System;
    using System.Collections.Generic;
    using OpenQA.Selenium;
    using OpenQA.Selenium.Chrome;
    using OpenQA.Selenium.Remote;
    using Xunit;
    
    namespace SeleniumTests;
    
    public class GoogleTest : IDisposable
    {
        private readonly IWebDriver _driver;
        private readonly string _sessionId;
    
        public GoogleTest()
        {
            // Configure Chrome options
            var options = new ChromeOptions();
            options.BrowserVersion = "latest";
            options.PlatformName = "WIN11";
            options.AddAdditionalOption("tb:options", new Dictionary<string, object>
            {
                ["key"] = Environment.GetEnvironmentVariable("TESTINGBOT_KEY") ?? "",
                ["secret"] = Environment.GetEnvironmentVariable("TESTINGBOT_SECRET") ?? "",
                ["name"] = "xUnit Google Test"
            });
    
            // Connect to TestingBot
            _driver = new RemoteWebDriver(
                new Uri("https://hub.testingbot.com/wd/hub"),
                options
            );
            _sessionId = ((RemoteWebDriver)_driver).SessionId.ToString();
        }
    
        [Fact]
        public void GoogleTitle_ShouldContainGoogle()
        {
            _driver.Navigate().GoToUrl("https://www.google.com");
            Assert.Contains("Google", _driver.Title);
        }
    
        public void Dispose()
        {
            // Quit also disposes the underlying remote session
            _driver?.Quit();
        }
    }

Run your test with:

    TESTINGBOT_KEY=your_key TESTINGBOT_SECRET=your_secret dotnet test

## Data-Driven Testing with Theory

xUnit's `[Theory]` attribute allows you to run the same test with different data. This is perfect for cross-browser testing:

    using OpenQA.Selenium;
    using OpenQA.Selenium.Chrome;
    using OpenQA.Selenium.Firefox;
    using OpenQA.Selenium.Edge;
    using OpenQA.Selenium.Remote;
    using Xunit;
    
    namespace SeleniumTests;
    
    public class CrossBrowserTest : IDisposable
    {
        private IWebDriver? _driver;
    
        [Theory]
        [InlineData("chrome", "latest", "WIN11")]
        [InlineData("firefox", "latest", "WIN11")]
        [InlineData("MicrosoftEdge", "latest", "WIN11")]
        [InlineData("safari", "latest", "SONOMA")]
        public void HomePage_ShouldLoad_OnAllBrowsers(string browser, string version, string platform)
        {
            // Create browser-specific options
            DriverOptions options = browser.ToLower() switch
            {
                "chrome" => new ChromeOptions(),
                "firefox" => new FirefoxOptions(),
                "microsoftedge" => new EdgeOptions(),
                "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"] = $"Cross-browser test - {browser}"
            });
    
            _driver = new RemoteWebDriver(
                new Uri("https://hub.testingbot.com/wd/hub"),
                options
            );
    
            _driver.Navigate().GoToUrl("https://testingbot.com");
            Assert.Contains("TestingBot", _driver.Title);
        }
    
        public void Dispose()
        {
            _driver?.Quit();
        }
    }

## Specify Browsers & Devices

To run tests on specific browsers and platforms, configure the appropriate `DriverOptions` class.

    // Chrome on Windows
    var chromeOptions = new ChromeOptions();
    chromeOptions.BrowserVersion = "latest";
    chromeOptions.PlatformName = "WIN11";
    chromeOptions.AddAdditionalOption("tb:options", new Dictionary<string, object>
    {
        ["key"] = "API_KEY",
        ["secret"] = "API_SECRET"
    });
    
    var driver = new RemoteWebDriver(
        new Uri("https://hub.testingbot.com/wd/hub"),
        chromeOptions
    );

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

  ![OS selected](https://testingbot.com/assets/environments/svg/windows11-0e1b28bc0fdd5034d3e4d3dc8d346c500a8c6522facf4b45d0da56537c1f1c6d.svg) Windows 11 › ![Browser Selected](https://testingbot.com/assets/environments/svg/chrome-c4081ff447d2d898d4afcb8f074a907c960e6f007716c1a1d119eee6803c4042.svg) Chrome 139 

Loading environments...

Please wait while we load the available browsers and platforms.

    

## Testing Internal Websites

Use [TestingBot Tunnel](https://testingbot.com/support/tunnel) to securely test internal or staged websites that aren't publicly accessible.

**1.** [Download the tunnel](https://testingbot.com/support/tunnel) and start it:

    java -jar testingbot-tunnel.jar API_KEY API_SECRET

**2.** Update your test to connect through the tunnel (change the hub URL to `localhost:4445`):

    // Connect through the tunnel instead of directly to TestingBot
    var driver = new RemoteWebDriver(
        new Uri("http://localhost:4445/wd/hub"),
        options
    );
    
    // Now you can access internal URLs
    driver.Navigate().GoToUrl("http://internal.yourcompany.com");

## Run tests in Parallel

xUnit runs tests in parallel by default. Tests in different test classes run in parallel, while tests in the same class run sequentially.

### Configure Parallelism

Control parallel execution by adding an `xunit.runner.json` file to your project:

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

Make sure to include it in your `.csproj`:

    <ItemGroup>
      <Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>

### Using Test Collections

Group tests that shouldn't run in parallel using `[Collection]`:

    // Tests in the same collection run sequentially
    [Collection("Sequential")]
    public class SequentialTests1 { }
    
    [Collection("Sequential")]
    public class SequentialTests2 { }
    
    // Tests without a collection run in parallel with other classes

### 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

To report test results to TestingBot, use JavaScript execution to set the test status:

    public class TestBase : IDisposable
    {
        protected IWebDriver Driver { get; }
        private bool _testPassed = true;
    
        public TestBase()
        {
            var options = new ChromeOptions();
            options.BrowserVersion = "latest";
            options.PlatformName = "WIN11";
            options.AddAdditionalOption("tb:options", new Dictionary<string, object>
            {
                ["key"] = Environment.GetEnvironmentVariable("TB_KEY") ?? "",
                ["secret"] = Environment.GetEnvironmentVariable("TB_SECRET") ?? ""
            });
    
            Driver = new RemoteWebDriver(
                new Uri("https://hub.testingbot.com/wd/hub"),
                options
            );
        }
    
        protected void MarkTestFailed()
        {
            _testPassed = false;
        }
    
        public void Dispose()
        {
            if (Driver != null)
            {
                try
                {
                    var sessionId = ((RemoteWebDriver)Driver).SessionId.ToString();
                    using var client = new System.Net.Http.HttpClient();
                    var key = Environment.GetEnvironmentVariable("TESTINGBOT_KEY");
                    var secret = Environment.GetEnvironmentVariable("TESTINGBOT_SECRET");
                    var auth = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($"{key}:{secret}"));
                    client.DefaultRequestHeaders.Authorization =
                        new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", auth);
                    var content = new System.Net.Http.FormUrlEncodedContent(new[]
                    {
                        new KeyValuePair<string, string>("test[success]", _testPassed ? "1" : "0")
                    });
                    client.PutAsync($"https://api.testingbot.com/v1/tests/{sessionId}", content).Wait();
                }
                finally
                {
                    Driver.Quit();
                }
            }
        }
    }
    
    // Usage in test class
    public class MyTests : TestBase
    {
        [Fact]
        public void TestSomething()
        {
            try
            {
                Driver.Navigate().GoToUrl("https://example.com");
                Assert.Equal("Example Domain", Driver.Title);
            }
            catch
            {
                MarkTestFailed();
                throw;
            }
        }
    }

## Other C# Framework examples

- [NUnit](https://testingbot.com/support/web-automate/selenium/csharp/nunit)

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

- [PNunit](https://testingbot.com/support/web-automate/selenium/csharp/pnunit)

With PNUnit you can run several tests in parallel.

- [SpecFlow](https://testingbot.com/support/web-automate/selenium/csharp/specflow)

SpecFlow allows you to run Automated .NET tests using Cucumber-compatible Gherkin syntax.

- [MSTest](https://testingbot.com/support/web-automate/selenium/csharp/mstest)

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

- [Reqnroll](https://testingbot.com/support/web-automate/selenium/csharp/reqnroll)

Reqnroll is the successor to SpecFlow for BDD testing with Gherkin syntax.

### Looking for more help?

Have questions or need more information? Reach out via email or Slack.

[Email us](https://testingbot.com/contact/new)[Slack Join our Slack](https://join.slack.com/t/testingb0t/shared_invite/zt-3bcw9xch-jk19~6XPs_xBrsAgAedkCw)
