Automated testing with Selenium Web Driver in .NET

Selenium Web Driver is a popular tool used to automate the front-end testing of web applications. It uses drivers provided by various browsers (Chrome, Firefox, etc.) to simulate user actions, and is very powerful when combined with testing frameworks such as xUnit or NUnit.

In our case, we decided to use Selenium with .NET Core 3.1 and xUnit. Let me share with you what I’ve learned so far.

To get started, you will need to download the web driver for the browser you want to test. Note that each web driver is tied to a specific version of the browser (although this doesn’t seem to be uniformly enforced). Therefore, it is best to download the file that corresponds to your browser version.

For Chrome, the driver can be downloaded from https://sites.google.com/a/chromium.org/chromedriver/downloads. Extract chromedriver.exe into your project. In your code, you will use ChromeDriver from OpenQA.Selenium.Chrome.

For Firefox, the driver can be downloaded from https://github.com/mozilla/geckodriver. Extract geckodriver.exe into your project and use the FirefoxDriver class from OpenQA.Selenium.Firefox in your code.

For Microsoft Edge, go to https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ and download the appropriate installer under “Microsoft Edge” (not “Microsoft Edge Legacy”, unless you want to use a very old version of Edge). Extract msedgedriver.exe into your project. Warning: I had to rename this file to MicrosoftWebDriver.exe for Selenium to recognize it.

When instantiating the driver class, pass in the path of the containing directory for the driver EXE file, e.g.

var driver = new ChromeDriver("C:\Drivers\Chrome");

To use any of the web drivers supported by Selenium, add the NuGet package Selenium.WebDriver to your project:

dotnet add package Selenium.WebDriver

This gives you access to OpenQA.Selenium.Chrome, etc. You will likely also need some supporting classes, such as ExpectedConditions. You can get these from the DotNetSeleniumExtras.WaitHelpers package.

dotnet add package DotNetSeleniumExtras.WaitHelpers

Note: It appears there is a separate package, SeleniumExtras.WaitHelpers, for version 4 of the Selenium.WebDriver.

Incidentally, I found that my Firefox driver took several times longer than the Chrome or Edge drivers to perform the same tests. As per comments by user jimevans on issue 6597 on GitHub, it seems this has to do with how the Gecko driver listens on loopback addresses. Explicitly setting the host to “::1” on the Gecko service resolved this for me. This changes the initialization code for Firefox slightly, e.g.

var driverService = FirefoxDriverService.CreateDefaultService("C:\Drivers\Firefox");
driverService.Host = "::1";
var driver = new FirefoxDriver(driverService);

The Firefox driver should now be about as fast as Chrome and Edge. 🚀

When using Selenium on an environment that doesn’t have secure certificates configured, you might not want your tests to fail because of pages like these:

Your connection is not private

You can tell the Selenium web driver to automatically bypass such warnings by setting the AcceptInsecureCertificates option to true. This property lives on the options class that corresponds to the driver you are using, e.g. ChromeOptions, FirefoxOptions, EdgeOptions. (An instance of the options class is passed as the second argument when instantiating the web driver.)

Selecting items from dropdown boxes in C# is done using the SelectElement class (confusingly known as Select in libraries for other languages) from the OpenQA.Selenium.Support.UI namespace. You need to install the Selenium.Support NuGet package to use this:

dotnet add package Selenium.Support

This creates an ambiguity with DotNetSeleniumExtras when it comes to theExpectedConditions class, which is obsolete in this package. You can resolve this by using ExpectedConditions from SeleniumExtras.WaitHelpers and SelectElement from OpenQA.Selenium.Support.UI.

Your code might look something like this:

var countrySelect = new OpenQA.Selenium.Support.UI.SelectElement(driver.FindElementById("CountryId"));
countrySelect.SelectByText("South Africa");

Fortunately, Selenium doesn’t seem to have any trouble working with iframes, provided you tell it which frame to switch to.

To switch to the outer frame (i.e. the parent frame), you can do this:

driver.SwitchTo().DefaultContent();

To switch to a specific inner frame, you can do something like this:

driver.SwitchTo().Frame(driver.FindElementById("example-id"));

Once switched, the web driver will operate within the context of the selected frame. Don’t forget to switch back to the parent when you’re done!

Automating third party visual components can occasionally be very complex, and we’ve found it convenient to fall back on invoking JavaScript directly from our tests. Selenium allows you to execute arbitrary JavaScript by casting the web driver instance to IJavaScriptExecutor, e.g.

IJavaScriptExecutor js = driver;
js.ExecuteScript("coffeeMachine.makeCoffee()");

Warning: Avoid using JavaScript to simulate actions like click events, as doing so circumvents the power of Selenium. Ideally, you want your test to model what the user does as closely as possible.

On the subject of JavaScript, while the Chrome driver seems generally happy, the Firefox driver does not seem to appreciate me invoking jQuery directly in the scripts passed to ExecuteScript (it hangs and times out). Using it to fire off a vanilla JavaScript function that itself uses jQuery seems to be OK, however. 🤷‍♂️

There doesn’t seem to be any tricks to combining Selenium with xUnit. You can call the web driver directly from your test code and harness the power of the framework to organise and execute your automated tests. Here’s a useful blog post about some of the things you can do with xUnit.

If you want to run your tests without seeing a browser window pop-up, you can run the browser in headless mode. Selenium allows you to do this by calling the AddArgument method of the appropriate options class (discussed above) to send the following command-line argument to the browser:

--headless

Note that, at the time of writing, the Selenium bindings for Edge do not support AddArgument on theEdgeOptions class, so it is not possible to pass parameters to the browser in this way. However, this functionality should be included in the next release, as it is already available in the 4.0.0-alpha05 build.

Selenium Web Driver is a powerful tool for automated front-end testing, but there are some quirks to work through. Thankfully, there are lots of helpful resources online. I might do a follow-up article as I learn more about it, so keep a lookout!

  • Visual Studio Code 1.46.1
  • .NET Core 3.1.201
  • Selenium.WebDriver 3.141.0 (NuGet)
  • Selenium.Support 3.141.0 (NuGet)
  • Selenium.Essentials 1.0.2 (NuGet)
  • xUnit 2.4.0 (NuGet)
  • Chrome 83.0.4103.116
  • Firefox 77.0.1
  • Firefox Web Driver (Geckodriver) 71.0.0.7222
  • Edge 83.0.478.56
  • Edge Web Driver 83.0.478.56

Software developer walking the edge between legacy systems and modern technology. I also make music: https://soundcloud.com/stephanbester