How To Use WebDriverWait In Selenium C#
The more you work with Selenium automation, the more you will come across various exceptions, such as NoSuchElementException (when an element is not visible on the page) or StaleElementReferenceException. Or there will be times when you will try to interact with an element that is not yet in the state you expect it to be (e.g., clickable).
To do this, you can instruct Selenium WebDriver to wait for certain conditions to be met explicitly. Explicit waits in Selenium are a way to deal with the dynamic nature of web pages, where elements may take longer to load or become available for interaction. By using explicit waits, you can tell Selenium to wait for a specific condition to be met before executing the next step in your automation script. This allows for a more flexible and robust automation solution.
In this Selenium C# tutorial, I will show you how you can use the WebDriverWait in Selenium C# to increase the reliability of your test.
Run your Jest automation tests in massive parallel across multiple browser and OS combinations with LambdaTest, Read more.
What are Selenium Waits?
Before we dig deeper, let’s understand the waits in Selenium and why we use them. Selenium WebDriver doesn’t keep track of the DOM’s live, active state. Its default page load method waits for the document.readyState to become “complete”. This can happen before all the elements on the page are loaded and can cause the tests to throw NoSuchElementException. You can refer to this blog on the most common Selenium exceptions to learn more about it.
Consider a test where you try to click on a button that has not yet loaded or is not yet enabled (though visible) on the page. If Selenium WebDriver attempts to click on a button that is not available in the DOM, it will throw a NoSuchElementException. You might also witness the Element Is Not Clickable at Point exception if the element is visible in the DOM but not clickable at a particular point.
This will cause all further steps to fail, although the application works as expected when clicking the button at the correct time. To prevent this, we use waits. With waits, we can tell our tests to wait a certain amount or for specific conditions before moving on to the next steps.
Types of Waits in Selenium C
There are several ways to wait for a web element to appear on a web page. I will go through each one and then expand more on the explicit waits, which are the focus of this blog.
Thread.Sleep()
System.Threading.Thread.Sleep(milliseconds) is not a Selenium WebDriver method but a built-in one in .Net. What it does is it suspends the current thread for the number of milliseconds passed as a parameter.
However, this is not recommended when working with automated web UI testing, because it is very rigid — the specified time has to pass before moving to the next step, regardless of whether the element you are waiting for was loaded faster.
For example, if your web element takes 3 seconds to load, and the Sleep parameter is set to 5 seconds, the test will wait 5 seconds before moving to the next step:
Thread.Sleep(5000);
driver.FindElement(By.Id("slowButton")).Click();
Test your website or web app online for iOS browser compatibility, including on the latest iPhone Simulator, by using an iPhone tester. Perform seamless cross-browser testing on this device to ensure a flawless user experience. You can even try it for free.
It might not sound like a big deal, but if you have hundreds of tests and many of them use Thread.Sleep(), which can add a lot of unnecessary wait time to the test execution. If the test takes around 5 seconds, adding up the Sleep time of another 5 seconds, that means doubling the time it takes to run the test!
Also, if the time it takes for the button to load exceeds those 5 seconds, you will still get a NoSuchElementException.
Implicit Wait
Then, we have the Selenium implicit wait:
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(20);
The implicit wait in Selenium is dynamic — it waits until an element is visible on the page or until the given timespan has elapsed. If the element has not been found before the given time duration, an ElementNotVisibleException or NoSuchElementException will be thrown.
The Selenium implicit wait applies to all the web elements in the test script and is a much-preferred method to Thread.Sleep().
Explicit Wait
However, if you want even more flexibility, you should go for the explicit wait. The WebDriverWait in Selenium C# is part of the OpenQA.Selenium.Support.UI package.
WebDriverWait in Selenium C# allows Selenium not only to wait for an element to be visible on the web page but also until specific conditions are met.
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
wait.PollingInterval = TimeSpan.FromMilliseconds(200); wait.Until(ExpectedConditions.ElementIsVisible(By.Id("my-element']"))).Click();
A complete WebDriver tutorial that covers what WebDriver is, its features, architecture and best practices.
This is how WebDriverWait in Selenium C# works:
The condition is checked. If the condition is true, the next lines of code are executed.
If the condition is false, a thread sleep is called at a polling interval. The default value of this polling interval is 250 ms, but a different value can be specified:
wait.PollingInterval = TimeSpan.FromMilliseconds(200);
After this interval elapses, the condition is rechecked.
This step repeats until the given wait time expires or the condition is met.
Expected Conditions with Explicit Wait
Here are some examples of Expected Conditions in Selenium I have used the most in my tests (your tests might have different needs, but these are just to illustrate how explicit waits can be useful):
ElementExists: waits for the element to exist in the DOM of the page, even if it’s not visible.
ElementIsVisible: waits for the element to be visible on the page. There are cases when the element can exist in the DOM but is not yet displayed on the page, so we cannot interact with it.
ElementToBeClickable: wait for an element to become clickable — can be useful when a button is quickly loaded on the page but cannot be clicked right away because it is disabled or cannot be clicked until a different action is performed.
AlertIsPresent: waits for the presence of an alert — alerts are usually triggered by another action, and it can take some time to display them. If you interact with an element on the alert (like dismiss/confirm the alert or try to confirm its text) too soon, you can get a NoAlertPresentException.
InvisibilityOfElementLocated: waits for an element to not be visible on the page. For instance, you want to check that after inserting invalid data in an input field, the warning message is no longer displayed when the correct data is entered.
TextToBePresentInElement: waits for a given text to be displayed inside a certain web element.
ElementToBeSelected: waits for an element to be selected — can be used for checkboxes that are automatically selected through other actions.
Demonstration: How to use WebDriverWait in Selenium C
In this section, we will go over an example of using WebDriverWait in Selenium C# to handle the dynamic nature of web pages. The WebDriverWait class is a useful tool for waiting for specific conditions to be met before executing the next step in your automation script. This helps to make your scripts more flexible and robust, allowing for a better automation solution.
Test Scenario
Navigate to the JQuery Download Progress Bar page.
Click the Start Download button.
Click the Close button when it’s available:
Implementation
If this is your first test, you will need to create a new project. In this WebDriverWait in Selenium C# tutorial, I’ll be using the MSTest framework, which is Microsoft’s tool for running tests in .NET applications. If you prefer to work with NUnit, you can check out the Selenium NUnit tutorial.
To learn more about MSTest, NUnit, and other popular unit testing frameworks like xUnit, you can go through this blog on NUnit vs xUnit vs MSTest.
Finally, we will be executing the test on the LambdaTest cloud grid. Cloud testing platforms like LambdaTest allow you to perform cross browser testing at scale over an online device farm of 3000+ browsers and browser versions.
%[INVALID_URL]By using an iPhone emulator, such as the iOS simulator by LambdaTest, you can effortlessly test your websites and web applications across a range of devices, operating system versions, and browsers, from the latest models to older ones. Additionally, you can test your iOS app online across 200+ device and iOS environments. Register for free and start testing today.
Subscribe to LambdaTest YouTube Channel and stay updated with detailed tutorials around Selenium testing, Cypress testing, and more.
To create a new test project in Visual Studio, select the MSTest Test Project type:
Install the following NuGet packages from the Package Manager:
Selenium.WebDriver: For this WebDriverWait in Selenium C# tutorial, I am using version 3.141.0, which is still one of the most used versions in existing Selenium projects.
Selenium.Support: It should be the same version as the Selenium.WebDriver package; this is the package that contains the Selenium WebDriverWait.
- Install the NuGet packages from the UI, from the Tools menu → NuGet Package Manager → Manage NuGet Packages for Solution… and search for them in the list:
Or you can run the install command from the Package Manager Console (Tools menu → NuGet Package Manager → Package Manager Console):
PM> NuGet\Install-Package Selenium.WebDriver -Version 3.141.0
I used the LambdaTest Capabilities Generator to create the configuration where I want to run my tests. You can create your custom setup, depending on the configuration you want to use in your tests. I’m keeping it simple, testing on Windows 11 with Chrome version 101 and a resolution of 1280×800.
- Before running the tests, please set the environment variables LT_USERNAME & LT_ACCESS_KEY from the terminal. The account details are available on your LambdaTest Profile page.
For macOS:
export LT_USERNAME=LT_USERNAME
export LT_ACCESS_KEY=LT_ACCESS_KEY
For Linux:
export LT_USERNAME=LT_USERNAME
export LT_ACCESS_KEY=LT_ACCESS_KEY
For Windows:
set LT_USERNAME=LT_USERNAME
set LT_ACCESS_KEY=LT_ACCESS_KEY
- Create a new class containing the following code:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
using System;
using OpenQA.Selenium.Support.UI;
namespace SeleniumExplicitWaits
{
[TestClass]
public class SeleniumWebDriverWaits
{
private static IWebDriver driver;
public static string gridURL = "@hub.lambdatest.com/wd/hub";
public static string LT_USERNAME = Environment.GetEnvironmentVariable("LT_USERNAME");
public static string LT_ACCESS_KEY = Environment.GetEnvironmentVariable("LT_ACCESS_KEY");
[TestInitialize]
public void SetUp()
{
var desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.SetCapability("browserName", "Chrome");
desiredCapabilities.SetCapability("platform", "Windows 11");
desiredCapabilities.SetCapability("version", "101.0");
desiredCapabilities.SetCapability("screenResolution", "1280x800");
desiredCapabilities.SetCapability("user", LT_USERNAME);
desiredCapabilities.SetCapability("accessKey", LT_ACCESS_KEY);
desiredCapabilities.SetCapability("build", "Selenium C-Sharp");
desiredCapabilities.SetCapability("name", "Selenium Test");
driver = new RemoteWebDriver(new Uri($"https://{LT_USERNAME}:{LT_ACCESS_KEY}{gridURL}"), desiredCapabilities, TimeSpan.FromSeconds(600));
}
[TestMethod]
public void DownloadFile()
{
driver.Navigate().GoToUrl("https://www.lambdatest.com/selenium-playground/jquery-download-progress-bar-demo");
driver.FindElement(By.Id("downloadButton")).Click();
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
wait.PollingInterval = TimeSpan.FromMilliseconds(200);
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//button[contains(text(),'Close') and @class='ui-button ui-corner-all ui-widget']"))).Click();
}
[TestCleanup]
public void Cleanup()
{
driver.Quit();
}
}
Perform browser automation testing on the most powerful cloud infrastructure. Leverage LambdaTest automation testing platform for faster, reliable and scalable experience on cloud.
Code Walkthrough
The using statements in any C# class contain the packages that will be used:
The [TestClass] attribute from MSTest marks the class as a test class:
In the next part, I’m declaring all the variables that will be used in the methods:
The driver is a new instance of the IWebDriver interface. Through it, we control the browser.
The next lines of code are related to my LambdaTest Account because I want my tests to run on cloud Selenium Grid. This way, I can specify configurations where my tests will run, which can be different from my local configuration.
This brings us to the next part:
This method is triggered before each test case. It will create a new browser instance with specific capabilities.
The next method is the actual test:
The [TestMethod] attribute informs that the method is a test method. The Navigate().GoToUrl() is the Selenium C# method to go to a specific URL.
Then, the FindElement() in Selenium is the method that allows us to identify web elements on the web page based on locators, such as
ID
Name
Class Name
XPath
CSSSelector
Link text or partial link text
For the first element I’m interacting with, I’m using the ID locator in Selenium. It’s usually recommended because it should be unique. To retrieve this information from the browser, right-click the element and select Inspect. This will open up (if it’s not already open) the Developer Tools, where you can see the details of the element:
Next, I’m using the actual explicit wait:
I created a new variable of type WebDriverWait, giving it a time span of 20 seconds — this is the maximum amount of type I am instructing Selenium to wait for my condition to be met.
Next, I am using a polling interval of 200 milliseconds — this is completely optional, but I wanted to show you how it can be used. Selenium will retry to find the web element every 200 milliseconds.
Finally, I specified the condition I wanted to wait for, which was for the button to become clickable. The button doesn’t have an Id, so I am using an XPath with a combination of 2 attributes: text and class name. I can see this in Developer Tools as well:
<button type="button" class="ui-button ui-corner-all ui-widget">Close</button>
The last method is the cleanup, marked by the attribute [TestCleanup], which will run after every test. It simply closes the browser using the Selenium Quit() method.
[TestCleanup]
public void Cleanup()
{
driver.Quit();
}
Execution
The test will be visible in Visual Studio’s test explorer, and you can run it from there. If you choose to run it locally, you will see the browser open up and perform all the steps. If you are using cloud Selenium Grid, like me, you will see the test result when it finishes running:
And you will see the results online:
You can also see all the retries performed before the element was found:
Perform browser automation testing on the most powerful cloud infrastructure. Leverage LambdaTest testing automation cloud for faster, reliable and scalable experience on cloud.
However, my test was successful because after waiting less than the given time, the element was found and clickable.
Demonstrate your expertise and knowledge in using Selenium for C# automation testing with this Selenium C# 101 certification. This will enhance your technical skills and establish your credibility as a tester.
Conclusion
Having the option to wait for web elements to be loaded or meet a certain condition can be very important in automated UI testing.
In this blog on WebDriverWait in Selenium C#, I covered how to create explicit waits using WebDriverWait in Selenium C# to create more stable tests and why they can be preferred to implicit waits. I also showed how you can run the tests on the cloud, so you don’t have to use your local settings and configuration, and how to see the results on the LambdaTest cloud Selenium Grid.
Subscribe to my newsletter
Read articles from Andreea Draniceanu directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by