Contents

    Guides

    Cross-Browser Testing in Selenium C#: Tutorial

    Published on

    September 30, 2025
    Cross-Browser Testing in Selenium C#: Tutorial

    Cross-browser testing in Selenium C# ensures applications work consistently across different browsers and versions. It verifies functionality, layout, and performance so users get the same experience whether they use Chrome, Firefox, or Edge.

    With Selenium and C#, teams can automate tests efficiently, set up drivers, run tests in parallel with NUnit, and integrate them into CI/CD pipelines for continuous feedback.

    This article explains how to set up Selenium C# for cross-browser testing, manage drivers, configure NUnit, handle pipelines, and apply best practices.

    Cross-Browser Testing in Selenium C#: What to Know Before You Start

    Cross-browser testing in Selenium C# checks how an application behaves across different browsers. It helps detect differences in rendering engines, JavaScript execution, and element interaction that often lead to layout issues or broken workflows. These checks ensure the application works the same way for every user.

    Selenium WebDriver supports browser-specific drivers like ChromeDriver, GeckoDriver, and EdgeDriver. In C#, NUnit makes it possible to parameterize and run the same test across multiple browsers efficiently, without duplicating code. This improves coverage while keeping the framework maintainable.

    Before you start, make sure you have:

    • Updated browser versions
    • Matching driver binaries (ChromeDriver, GeckoDriver, EdgeDriver)
    • A structured test framework with clear folder organization
    • Setup and teardown methods for opening and closing browsers

    Having these basics in place reduces flaky tests and makes it easier to integrate with CI/CD pipelines.

    Setting Up Selenium C# for Cross-Browser Testing

    Cross-browser testing in C# begins with a reliable test environment. You need Visual Studio for development, Selenium WebDriver for browser automation, and NUnit for test execution. Using NuGet, you can add these dependencies directly to your project, which keeps package management consistent.

    A practical setup involves:

    • Visual Studio with .NET installed
    • Selenium.WebDriver and NUnit packages via NuGet
    • Browser drivers such as ChromeDriver, GeckoDriver, and EdgeDriver, stored in a dedicated folder
    • Base test class that initializes drivers and handles cleanup

    Here’s a minimal example of a base class in C#:

    using NUnit.Framework;

    using OpenQA.Selenium;

    using OpenQA.Selenium.Chrome;

    using OpenQA.Selenium.Firefox;

    using OpenQA.Selenium.Edge;

    namespace CrossBrowserTests

    {

        public class BaseTest

        {

            protected IWebDriver driver;

            [SetUp]

            public void Setup([Values("chrome", "firefox", "edge")] string browser)

            {

                switch (browser.ToLower())

                {

                    case "chrome":

                        driver = new ChromeDriver();

                        break;

                    case "firefox":

                        driver = new FirefoxDriver();

                        break;

                    case "edge":

                        driver = new EdgeDriver();

                        break;

                }

                driver.Manage().Window.Maximize();

            }

            [TearDown]

            public void Cleanup()

            {

                driver.Quit();

            }

        }

    }

    This structure ensures that every test run starts with a fresh browser instance and ends with proper cleanup. It also makes it easier to extend test cases to multiple browsers without duplicating logic.

    Managing Multiple Browser Drivers

    When scaling cross-browser tests in Selenium C#, managing multiple drivers efficiently is essential. Hardcoding paths for ChromeDriver, GeckoDriver, or EdgeDriver quickly become unmanageable. A better approach is to centralize driver configuration and load the required driver dynamically.

    Two common strategies are:

    • Driver Factory Pattern: Create a single class that returns the appropriate IWebDriver based on input.
    • Environment Variables / Config Files: Store driver paths and browser names in a config file or environment variable for easier updates.

    Here’s an example of a simple driver factory in C#:

    using OpenQA.Selenium;

    using OpenQA.Selenium.Chrome;

    using OpenQA.Selenium.Firefox;

    using OpenQA.Selenium.Edge;

    namespace CrossBrowserTests

    {

        public static class DriverFactory

        {

            public static IWebDriver GetDriver(string browser)

            {

                return browser.ToLower() switch

                {

                    "chrome" => new ChromeDriver(),

                    "firefox" => new FirefoxDriver(),

                    "edge" => new EdgeDriver(),

                    _ => throw new ArgumentException("Browser not supported: " + browser)

                };

            }

        }

    }

    Then in your test class, you can call:

    driver = DriverFactory.GetDriver("chrome");

    This avoids repetitive setup code and makes it easy to switch browsers through a single parameter. For larger projects, you can extend the factory to include options like headless mode, custom profiles, or remote WebDriver execution with Selenium Grid.

    Configuring NUnit for Parallel Test Execution

    Parallel test execution requires three things: configure NUnit to run workers, make sure each test has its own WebDriver instance, and avoid shared state between threads.

    1. Set NUnit assembly settings

    Define the number of parallel workers for the entire test run. Add this attribute in AssemblyInfo.cs or any compiled source file:

    using NUnit.Framework;

    [assembly: LevelOfParallelism(4)]

    Adjust 4 to match the number of cores on your CI agent or local machine.

    2. Use NUnit parallel attributes

    Mark tests or fixtures for parallel execution with the right scope:

    • ParallelScope.Fixtures runs different test fixtures in parallel.
    • ParallelScope.Children runs tests inside the same fixture in parallel.

    Example:

    [TestFixture]

    [Parallelizable(ParallelScope.Children)]

    public class SampleTests : BaseTest

    {

        // tests here

    }

    3. Ensure thread-safe WebDriver instances

    Never share a single IWebDriver across threads. Use ThreadLocal<IWebDriver> so each test thread has its own instance.

    Example base class:

    public class BaseTest

    {

        private static ThreadLocal<IWebDriver> _threadDriver = new ThreadLocal<IWebDriver>();

        protected IWebDriver driver => _threadDriver.Value;

        [SetUp]

        public void Setup([Values("chrome","firefox","edge")] string browser)

        {

            _threadDriver.Value = DriverFactory.GetDriverWithOptions(browser);

            driver.Manage().Window.Maximize();

        }

        [TearDown]

        public void Cleanup()

        {

            try { _threadDriver.Value?.Quit(); }

            finally { _threadDriver.Value = null; }

        }

    }

    DriverFactory.GetDriverWithOptions should return a fresh driver instance configured for the requested browser.

    Running Tests in CI/CD Pipelines

    Cross-browser tests are most valuable when they run automatically during every build. Integrating Selenium C# tests with a CI/CD pipeline ensures that browser compatibility is validated before changes reach production. 

    1. Add test execution to your CI pipeline

    Most CI/CD tools (GitHub Actions, Azure DevOps, Jenkins, GitLab) let you add a test stage. This stage should install drivers, restore NuGet packages, and run NUnit tests.

    2. Install browsers and drivers on the agent

    Make sure the CI agent has Chrome, Firefox, and Edge available. Use pre-installed drivers or download them as part of the pipeline. For example, in GitHub Actions:

    - name: Setup ChromeDriver

      uses: nanasess/setup-chromedriver@v2

    3. Run NUnit tests with dotnet

    Execute your cross-browser tests using the .NET test runner:

    dotnet test CrossBrowserTests.csproj --logger "trx"

    4. Publish test results

    Store the NUnit test results so the CI system can display them in the build report. For Azure DevOps:

    - task: PublishTestResults@2

      inputs:

        testResultsFormat: NUnit

        testResultsFiles: '**/TestResult.xml'

    5. Scale with Selenium Grid or cloud providers

    For large projects, integrate Selenium Grid or a cloud service (like BrowserStack) to run tests across many browsers and OS combinations simultaneously.

    Here’s a ready-to-use GitHub Actions workflow that runs your Selenium C# cross-browser tests with a browser matrix. It assumes your test setup reads the browser from the BROWSER environment variable (or uses a driver manager library at runtime). Short prelude, then the YAML.

    Use this as /.github/workflows/crossbrowser-tests.yml

    name: Cross-browser Tests

    on:

      push:

        branches: [ main ]

      pull_request:

        branches: [ main ]

    jobs:

      test:

        runs-on: ubuntu-latest

        strategy:

          fail-fast: false

          matrix:

            browser: [ chrome, firefox, edge ]

            dotnet-version: [ '7.0' ]

        env:

          # optional: override if needed

          TEST_RESULTS_DIR: test-results

        steps:

          - name: Checkout

            uses: actions/checkout@v4

          - name: Setup .NET

            uses: actions/setup-dotnet@v4

            with:

              dotnet-version: ${{ matrix.dotnet-version }}

          - name: Restore NuGet packages

            run: dotnet restore

          - name: Build

            run: dotnet build --configuration Release

          # optional: ensure ChromeDriver available when running chrome

          - name: Setup ChromeDriver

            if: matrix.browser == 'chrome'

            uses: nanasess/setup-chromedriver@v2

            with:

              chromedriver-version: latest

          # if you use a runtime driver manager inside tests (recommended),

          # you can skip extra driver install steps. Otherwise add similar

          # steps for geckodriver or edgedriver as needed.

          - name: Run tests on ${{ matrix.browser }}

            env:

              BROWSER: ${{ matrix.browser }}

              DOTNET_CLI_TELEMETRY_OPTOUT: 1

            run: |

              mkdir -p $TEST_RESULTS_DIR

              dotnet test ./CrossBrowserTests/CrossBrowserTests.csproj \

                --configuration Release \

                --logger "trx;LogFileName=${{ matrix.browser }}.trx" \

                --results-directory $TEST_RESULTS_DIR

          - name: Upload test results

            uses: actions/upload-artifact@v4

            with:

              name: test-results-${{ matrix.browser }}

              path: ${{ env.TEST_RESULTS_DIR }}/*${{ matrix.browser }}*.trx

    Notes and quick tips

    • Make your BaseTest.Setup read Environment.GetEnvironmentVariable("BROWSER") so the CI matrix controls the browser.
    • Use a driver manager library at runtime (for example, a WebDriverManager .NET package) to avoid fragile manual driver installs.
    • ubuntu-latest already ships Chrome and Firefox. For Edge, add an install step or use a cloud provider.
    • To scale beyond a single agent, run tests against Selenium Grid or a cloud provider, and change DriverFactory to create remote drivers.

    Challenges in Selenium C# Cross-Browser Testing

    Cross-browser testing with Selenium C# introduces issues that teams must plan for:

    1. Locator inconsistencies: Elements may have different IDs or DOM structures across browsers, causing selectors to break.
    2. Timing issues: JavaScript execution and rendering speeds vary, making tests unstable without proper waits.
    3. Driver maintenance: ChromeDriver, GeckoDriver, and EdgeDriver must be updated frequently to match browser versions.
    4. Parallel execution overhead: Running multiple browsers at once in NUnit can quickly exhaust CPU and RAM on CI servers.
    5. Limited local coverage: Local setups cannot replicate all OS–browser combinations, which limits test reliability without Grid or cloud execution.

    Best Practices for Cross-Browser Testing in Selenium C#

    A reliable framework requires specific technical practices rather than generic guidelines:

    1. Centralize browser setup: Use a driver factory that configures Chrome, Firefox, and Edge with options like headless mode, custom profiles, or remote WebDriver endpoints.
    2. Automate driver updates: Integrate a driver manager library to download the correct driver binaries at runtime, avoiding failures from version mismatches.
    3. Parameterize tests properly: Use NUnit’s [TestCase] or [TestFixtureSource] to run the same test across multiple browsers without duplicating logic.
    4. Apply explicit waits strategically: Replace hard-coded sleeps with WebDriverWait for elements that load asynchronously or behave differently in each browser.
    5. Isolate configuration files: Store browser names, timeouts, and base URLs in external config or environment variables so the test code remains stable.
    6. Control resource usage: Limit parallel threads to what the CI agent can handle, or offload execution to Selenium Grid or a cloud provider for large suites.

    Conclusion

    Cross-browser testing in Selenium C# is not limited to running scripts on Chrome, Firefox, or Edge. A strong framework must handle locator differences, keep drivers in sync, and support parallel execution to avoid flakiness. NUnit with a driver factory and parameterized execution provides that foundation.

    Local drivers only cover the browsers installed on your machine, and they cannot replicate older versions, multiple operating systems, or mobile environments. BrowserStack removes these limits by giving you instant access to 3,500+ real browsers and devices in the cloud. With built-in parallel execution, video recordings, screenshots, and detailed logs, it lets you validate C# tests at scale and debug issues faster than maintaining your own infrastructure.

    Run Selenium Tests on Cloud

    Data-rich bug reports loved by everyone

    Get visual proof, steps to reproduce and technical logs with one click

    Make bug reporting 50% faster and 100% less painful

    Rating LogosStars
    4.6
    |
    Category leader

    Liked the article? Spread the word

    Put your knowledge to practice

    Try Bird on your next bug - you’ll love it

    “Game changer”

    Julie, Head of QA

    star-ratingstar-ratingstar-ratingstar-ratingstar-rating

    Overall rating: 4.7/5

    Try Bird later, from your desktop