C# Selenium
last modified October 18, 2026
In this article we show how to automate web browsers and test web applications with the Selenium framework in C#.
Selenium
Selenium is a portable framework for testing web applications. Selenium runs on Windows, Linux, and macOS.
Selenium WebDriver is a collection of open source APIs which are used to automate the testing of a web application. Each browser is controlled through its own driver: ChromeDriver for Chrome, EdgeDriver for Microsoft Edge, GeckoDriver for Firefox, and SafariDriver for Safari. Selenium WebDriver supports several programming languages including C#, Java, Python, and JavaScript.
Selenium can run the browser in normal mode or in headless mode. In headless mode, the browser runs without a visible window, which is the usual setup for automated tests and CI servers.
In this article we use Microsoft Edge. The examples work with Chrome as well;
we replace EdgeOptions and EdgeDriver with
ChromeOptions and ChromeDriver.
Selenium Manager
In the past, the browser driver had to be downloaded manually and placed on
the PATH. Since version 4.6, Selenium contains
Selenium Manager, which finds the installed browser, downloads the
matching driver, and caches it in the ~/.cache/selenium directory.
We only need to have the browser installed.
$ dotnet add package Selenium.WebDriver
We add the Selenium.WebDriver package to the project. It also
contains the WebDriverWait class used for explicit waits; the
older DotNetSeleniumExtras.WaitHelpers package is no longer
maintained and is not needed.
C# Selenium simple example
In the first example, we get the title of a web page.
using OpenQA.Selenium.Edge;
var options = new EdgeOptions();
options.AddArgument("--headless=new");
using var driver = new EdgeDriver(options);
driver.Navigate().GoToUrl("https://example.com");
Console.WriteLine(driver.Title);
The program runs the browser in headless mode and prints the title of the page.
var options = new EdgeOptions();
options.AddArgument("--headless=new");
We pass a command line argument to the browser to run it in headless mode.
using var driver = new EdgeDriver(options);
We create an instance of the EdgeDriver. It starts the driver and
a new browser session. The driver is disposed at the end of the program; the
Dispose method calls Quit, which closes the browser
and stops the driver process.
driver.Navigate().GoToUrl("https://example.com");
Console.WriteLine(driver.Title);
We navigate to the given URL and get the title. The GoToUrl method
waits until the page is loaded.
$ dotnet run Example Domain
C# Selenium click button
In the following example, we click on a button and retrieve the text of an alert dialog.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test page</title>
</head>
<body>
<button id="mybtn" onclick="setTimeout(() => alert('Hello there!'), 500)">
Click
</button>
</body>
</html>
The HTML file contains a button which shows an alert dialog after half a second.
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Support.UI;
var options = new EdgeOptions();
options.AddArgument("--headless=new");
using var driver = new EdgeDriver(options);
var url = new Uri(Path.GetFullPath("index.html")).AbsoluteUri;
driver.Navigate().GoToUrl(url);
var button = driver.FindElement(By.Id("mybtn"));
button.Click();
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
wait.IgnoreExceptionTypes(typeof(NoAlertPresentException));
IAlert alert = wait.Until(d => d.SwitchTo().Alert());
Console.WriteLine(alert.Text);
alert.Accept();
We load the HTML file from the local file system.
var url = new Uri(Path.GetFullPath("index.html")).AbsoluteUri;
driver.Navigate().GoToUrl(url);
We build a file:// URL from the path of the HTML file. The file
is located in the current working directory, which is the project directory
when we use dotnet run.
var button = driver.FindElement(By.Id("mybtn"));
button.Click();
We find the button by its id and press it with Click.
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5)); wait.IgnoreExceptionTypes(typeof(NoAlertPresentException)); IAlert alert = wait.Until(d => d.SwitchTo().Alert());
The alert does not appear immediately. We use an explicit wait: the
Until method repeatedly calls the given function until it returns
a non-null value or the timeout expires. Until the alert pops up,
SwitchTo().Alert throws NoAlertPresentException, which
we tell the wait to ignore. Explicit waits are preferred over fixed delays with
Thread.Sleep, which make tests slow and flaky.
Console.WriteLine(alert.Text); alert.Accept();
We print the alert's text and close the dialog.
$ dotnet run Hello there!
C# Selenium JavaScript and screenshots
Selenium can execute JavaScript code in the page and take screenshots.
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
var options = new EdgeOptions();
options.AddArgument("--headless=new");
options.AddArgument("--window-size=1024,768");
using var driver = new EdgeDriver(options);
driver.Navigate().GoToUrl("https://example.com");
var js = (IJavaScriptExecutor) driver;
var title = js.ExecuteScript("return document.title;");
var state = js.ExecuteScript("return document.readyState;");
Console.WriteLine($"title: {title}, state: {state}");
js.ExecuteScript("document.body.style.background = 'lightyellow';");
Screenshot shot = driver.GetScreenshot();
shot.SaveAsFile("example.png");
Console.WriteLine("Screenshot saved");
We read two values from the page, change its background colour, and save a screenshot.
options.AddArgument("--window-size=1024,768");
We set the size of the browser window; it determines the size of the screenshot.
var js = (IJavaScriptExecutor) driver;
var title = js.ExecuteScript("return document.title;");
The ExecuteScript method runs JavaScript code in the context of
the current page. The value of the return statement is converted
to a .NET object.
Screenshot shot = driver.GetScreenshot();
shot.SaveAsFile("example.png");
We take a screenshot of the visible part of the page and save it as a PNG file.
$ dotnet run title: Example Domain, state: complete Screenshot saved
C# Selenium testing paragraph contents
Selenium is usually used together with a testing framework. In the next example, we test the contents of two paragraphs with NUnit.
$ dotnet new nunit -o WebTests $ cd WebTests $ dotnet add package Selenium.WebDriver
We create an NUnit test project and add Selenium.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My html page</title>
</head>
<body>
<p>
Today is a beautiful day. We go swimming and fishing.
</p>
<p>
Hello there. How are you?
</p>
</body>
</html>
This is the page under test.
<ItemGroup> <None Include="pages/**" CopyToOutputDirectory="PreserveNewest" /> </ItemGroup>
In the project file, we copy the pages directory to the output
directory, where the tests run.
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
namespace WebTests;
public class ParagraphTests
{
private IWebDriver driver = null!;
[SetUp]
public void Setup()
{
var options = new EdgeOptions();
options.AddArgument("--headless=new");
driver = new EdgeDriver(options);
}
[Test]
public void TestParagraphs()
{
var path = Path.Combine(TestContext.CurrentContext.TestDirectory,
"pages", "index.html");
driver.Navigate().GoToUrl(new Uri(path).AbsoluteUri);
var paras = driver.FindElements(By.TagName("p"));
Assert.That(driver.Title, Is.EqualTo("My html page"));
Assert.That(paras, Has.Count.EqualTo(2));
Assert.That(paras[0].Text, Does.Contain("Today is a beautiful day."));
Assert.That(paras[1].Text, Does.Contain("How are you?"));
}
[TearDown]
public void TearDown() => driver.Dispose();
}
A new browser is started before each test in the SetUp method and
closed in the TearDown method.
var path = Path.Combine(TestContext.CurrentContext.TestDirectory,
"pages", "index.html");
The TestDirectory property contains the directory of the test
assembly.
var paras = driver.FindElements(By.TagName("p"));
The FindElements method returns all matching elements. Unlike
FindElement, it does not throw an exception when nothing is found;
it returns an empty collection.
Assert.That(paras[0].Text, Does.Contain("Today is a beautiful day."));
The Text property returns the visible text of the element, with
leading and trailing whitespace removed.
C# Selenium submit form
In the next test, we fill in and submit a form. We use the test form of the httpbin.org service, which echoes the submitted data back as JSON.
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Support.UI;
namespace WebTests;
public class FormTests
{
private IWebDriver driver = null!;
[SetUp]
public void Setup()
{
var options = new EdgeOptions();
options.AddArgument("--headless=new");
driver = new EdgeDriver(options);
}
[Test]
public void TestSubmitForm()
{
driver.Navigate().GoToUrl("https://httpbin.org/forms/post");
driver.FindElement(By.Name("custname")).SendKeys("John Doe");
driver.FindElement(By.Name("custemail")).SendKeys("john.doe@example.com");
driver.FindElement(By.CssSelector("input[name='size'][value='medium']")).Click();
driver.FindElement(By.CssSelector("input[value='cheese']")).Click();
driver.FindElement(By.Name("comments")).SendKeys("Ring twice");
driver.FindElement(By.TagName("button")).Click();
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.Url.EndsWith("/post"));
var content = driver.FindElement(By.TagName("body")).Text;
TestContext.Out.WriteLine(content);
Assert.That(content, Does.Contain("\"custname\": \"John Doe\""));
Assert.That(content, Does.Contain("\"size\": \"medium\""));
Assert.That(content, Does.Contain("\"topping\": \"cheese\""));
}
[TearDown]
public void TearDown() => driver.Dispose();
}
We fill in text fields, select a radio button and a check box, and submit the form.
driver.FindElement(By.Name("custname")).SendKeys("John Doe");
We locate the input box by its name attribute and type the text
into it with SendKeys.
driver.FindElement(By.CssSelector("input[name='size'][value='medium']")).Click();
driver.FindElement(By.CssSelector("input[value='cheese']")).Click();
Radio buttons and check boxes are selected by clicking them. We locate them with CSS selectors.
driver.FindElement(By.TagName("button")).Click();
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.Url.EndsWith("/post"));
We click on the submit button and wait until the browser navigates to the response page.
var content = driver.FindElement(By.TagName("body")).Text;
TestContext.Out.WriteLine(content);
We get the text of the response. The output written to
TestContext.Out is shown when we run the tests with detailed
verbosity.
Assert.That(content, Does.Contain("\"custname\": \"John Doe\""));
The assertions check that the server received the submitted values.
$ dotnet test ... Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2, Duration: 1 s - WebTests.dll (net10.0)
Both tests pass. With dotnet test --logger "console;verbosity=detailed",
we can also see the JSON returned by httpbin.org.
Source
Selenium WebDriver documentation
In this article we have worked with the Selenium web testing framework in C#.
Author
List all C# tutorials.