What is Static Waiting in Selenium WebDriver

Tips to avoid brittle UI tests

In the last article, I talked about some ideas and patterns like the Page Object pattern that help in writing serviceable UI tests. This article covers some advanced topics that can help you write more robust tests and troubleshoot problems:

  • We'll discuss why adding fixed delays in UI tests is a bad idea and how to get rid of them.
  • Browser automation frameworks target user interface elements with the help of selectors, and it is very important to use good selectors to avoid brittle testing. So I'm going to give you some tips if you want to pick the right selectors and target elements right away.
  • UI tests fail more often than other types of tests. So how can we debug a broken UI test and find out what caused it to fail? In this section, I'll show you how to capture a screenshot and the HTML source code of the page when a UI test fails. This makes it easier for you to examine it.

I'll be using Selenium for the browser automation topics covered in this article.

Similar to the previous article, the concepts and solutions discussed in this article are applicable regardless of the language and UI framework used. Before proceeding, please read the previous article as I will be referencing it and its sample code a few times. Do not worry; I'll wait here.


Don't add delays to your tests

Adding (or delays in general) feels like an inevitable hack when testing UI. You have a test that intermittently fails and after doing some research you attribute it to occasional delays in the response. For example, you navigate to a page and do a search or confirm something before the page fully loads and your browser automation framework throws an exception stating that the element does not exist. Many things could contribute to this delay. For example:

  • The web server, database and / or network are overloaded and busy with other requirements.
  • The tested page is slow because it loads a lot of data and / or queries a lot of tables.
  • You are waiting for an event on the site that will take time.

Or a mixture of these and other topics.

Let's say you have a page that usually takes less than a second to load, but the tests that pass it occasionally fail because the response is occasionally delayed. You have a few options:

  • They don't add any delay: in this case, the tests that hit this page will sometimes fail, reducing the confidence in the tests.
  • You're adding a one-second delay to the tests that hit that page: in this case everything of these tests are always Even if this takes a second longer, even if the page loads quickly, the tests are not guaranteed to be successful because the page sometimes takes longer than a second.
  • You may choose to delay a few seconds: this will ensure that the page will definitely always load. Now, however, the UI test is taking longer and longer.

You see, there is no win with arbitrary delays: you get either a slow test suite or a brittle test suite. Here, I'll show you how to avoid adding fixed delays to your tests. We're going to discuss two types of delays that should cover pretty much any cases you need to deal with: add a global delay and wait for something to happen.

Add global delay

If all of your pages take about the same time to load, which is longer than expected, then most of the tests will fail due to an untimely response. In such cases, you can use implicit wait times:

One implicit wait is to tell WebDriver to poll the DOM for a specified amount of time when trying to find an item or items if they are not immediately available. The default setting is 0. Once set, the implicit wait time for the lifetime of the WebDriver object instance is set.

To set an implicit wait time:

WebDriver driver = new FirefoxDriver (); driver.Manage (). Timeouts (). Implicitly wait (TimeSpan.FromSeconds (5));

This is how you tell Selenium to wait up to 5 seconds when trying to find an item or interact with the page. So now you can write:

driver.Url = "http: // somedomain / url_that_delays_loading"; IWebElement myDynamicElement = driver.FindElement (By.Id ("someDynamicElement"));

instead of:

driver.Url = "http: // somedomain / url_that_delays_loading"; Thread.Sleep (5000); IWebElement myDynamicElement = driver.FindElement (By.Id ("someDynamicElement"));

The advantage of this approach is that it returns as soon as the item is found, rather than waiting the entire 5 seconds if the item is available sooner.

As soon as the implicit waiting for your is set, it applies to all actions on the driver, for example. so you can get rid of lots of s in your code.

5 seconds is a wait that I saved for this article - you should find the optimal implicit wait for your application and make that wait as short as possible. From the API documentation:

Increasing the implicit wait time limit should be used sensibly as it negatively affects the test run time, especially with slower location strategies like XPath.

Even if you're not using XPath, using long implicit waits will slow down the tests, especially if some tests actually fail because the web driver will wait a long time before timing out and throwing an exception.

Waiting for explicit events / changes

Implicit waiting is a great way to eliminate a lot of hard coded delays in your code. However, you will still find yourself in a situation where you have to add some fixed delays in your code as you wait for something to happen: one page is slower than all the other pages and you have to wait longer for one to end AJAX call or the appearance or disappearance of an element from the page, etc. Here you have to wait explicitly.

Explicit waiting

So you've set the implicit wait to 5 seconds and it works for a lot of your tests. However, there are still a few pages that sometimes take longer than 5 seconds and result in failed tests.

As a side note, you should investigate why a page is taking so long first before attempting to fix the failed test by making it wait longer. There may be a performance issue on the side leading to the red test. In this case, you should correct the page, not the test.

In the case of a slow side, you can replace fixed delays with explicit waiting times:

An explicit wait is code that you define to wait for a specific condition to occur before continuing in your code.

You can apply explicit waits with class. lives in assembly and can be installed with Selenium.Support Nuget:

/// public class WebDriverWait: DefaultWait

Here is an example how you can use it in your tests:

driver.Url = "http: // somedomain / url_that_takes_a_long_time_to_load_de"; WebDriverWait wait = new WebDriverWait (driver, TimeSpan.FromSeconds (10)); var myDynamicElement = wait.Until (d => d.FindElement (By.Id ("someElement")));

We tell Selenium we want it to wait up to 10 seconds for that particular page / element.

You will likely have a few pages that are taking longer than the standard implicit wait time. Repeating this code all over is not a good coding method. At long last Test code is code. You can instead extract this into a method and use it from your tests:

public IWebElement FindElementWithWait (By by, int secondsToWait = 10) var wait = new WebDriverWait (WebDriver, TimeSpan.FromSeconds (secondsToWait)); return wait.Until (d => d.FindElement (by));

Then you can use this method as:

var slowPage = new SlowPage ("http: // somedomain / url_that_takes_a_long_time_to_load"); var element = slowPage.FindElementWithWait (By.Id ("someElement"));

This example shows what the method could possibly look like and how it could be used. Ideally, you would move all page interactions to your page objects.

Alternative explicit waiting example

Let's look at another example of an explicit wait. Sometimes the page loads completely but the item doesn't yet exist because it will be loaded later as a result of an AJAX request. Maybe it's not an item you're waiting for, you just want to wait for an AJAX interaction to finish before you can make an assertion, e.g. B. in the database. Again, most developers use B. Make sure the AJAX call is complete and the record is now in the database before moving on to the next line of the test. This can easily be corrected by running JavaScript!

Most browser automation frameworks allow you to run JavaScript in the active session. Selenium is no exception. In Selenium there is an interface called with two methods:

/// public interface IJavaScriptExecutor /// /// The JavaScript code to be executed.< object="" executescript="" (zeichenkettenskript,="" params="" object="" []="" args);="" der="" auszuf├╝hrende="">< object="" executeasyncscript="" (zeichenfolgen-skript,="" params="" object="" []="" args);="">

This interface is implemented by This is the base class for all web driver implementations. So you can call a JavaScript script from your web driver instance. You can use this method to wait for all AJAX calls to finish (assuming you are using jQuery):

// It is assumed that it is in a class that can access the active instance 'WebDriver' via the field / property 'WebDriver'. public void WaitForAjax (int secondsToWait = 10) var wait = new WebDriverWait (WebDriver, TimeSpan.FromSeconds (secondsToWait)); wait.Until (d => (bool) ((IJavaScriptExecutor) d) .ExecuteScript ("return jQuery.active == 0"));

Combine that with and you can get rid of added AJAX calls.

returns the number of active AJAX calls initiated by jQuery; So if it's null then no AJAX calls are made. Obviously, this method only works if all AJAX requests are initiated by jQuery. If you are using other JavaScript libraries for AJAX communication, you should search the appropriate API documentation for an equivalent method or follow AJAX calls yourself.

Expected condition

Explicit wait allows you to set a condition and wait for it to be met or to time out. We saw how we can check whether AJAX calls should be terminated. Another example is the visibility of an element. Just like with the AJAX check, you can write a condition that checks the visibility of an item. There is a simpler solution for this.

From the documentation of selenium:

When automating web browsers, some conditions are common.

If you are using Java, you are in luck. Class in Java is quite extensive and has many convenience methods. You can find the documentation here.

.Net developers are not that happy. There's still one class under assembly (documented here), but it's very minimal:

public sealed class ExpectedConditions /// /// The expected title, which must match exactly. /// public static function

You can use this class in combination with:

var wait = new WebDriverWait (Driver, TimeSpan.FromSeconds (3)) var Element = wait.Until (ExpectedConditions.ElementExists (By.Id ("foo")));

As you can see from the class signature above, you can use the title or parts of it to check the presence and visibility of elements. Immediate support in .Net may be very poor. But this class is nothing more than a wrapper for some simple conditions. You can just as easily implement and use other common conditions in a class from your test scripts.

FluentWait

Another gem only for Java developers is this. From the documentation page, is

An implementation of the wait interface in which the time limit and the query interval can be configured during operation. Each FluentWait instance defines the maximum waiting time for a condition and the frequency with which the condition should be checked. In addition, the user can configure the wait to ignore certain types of exceptions while the wait is in progress, e.g. B. NoSuchElementExceptions when looking for an element on the page.

In the following example we are trying to find an item with the ID on the page accessible every five seconds up to 30 seconds:

// Wait 30 seconds for an element to be on the page and check // every five seconds for it. Waiting

There are two great things about it: one, you can specify the polling interval that could improve test performance, and the other, you can ignore the exceptions that you are not interested in.

is pretty awesome and it would be cool if there was an equivalent in .Net too. That said, it's not that difficult to implement with.


Choose the right selectors

You have your page objects set up, have nice DRY maintainable test code, and avoid fixed delays in your tests. but your tests still fail!

The user interface is usually the most frequently changed part of a typical application: sometimes you move elements on a page to change the design of the page, and sometimes the page structure changes according to needs. These changes to page layout and design can lead to a lot of testing if you don't choose your selectors wisely.

Don't use fuzzy selectors or rely on the structure of your page.

Many times I've been asked if it's okay to add an ID to the elements on the page just for testing, and the answer is a resounding yes. In order to make our code unit testable, we make a lot of changes, e.g. B. adding interfaces and using dependency injection. Test code is code. Do whatever it takes to aid your testing.

Let's say we have a page with the following list:

  • The best of men at work
  • For those who like rock, we greet you
  • Let there be rock

In one of my tests, I want to click on the Let There Be Rock album. I would ask for trouble if I used the following selector:

By.XPath ("// ul [@ id = 'album-list'] / li [3] / a")

Whenever possible, add an ID to the elements and target them directly without relying on the surrounding elements. So I'm going to make a small change to the list:

  • The best of men at work
  • For those who like rock, we greet you
  • Let there be rock

I've added attributes to anchors based on the unique album id so we can target a link directly without going through and items. So I can now replace the brittle selector with what is guaranteed to work as long as the album is on the side, which is also a good claim, by the way. In order to create this selector, I would of course have to be able to access the album ID via the test code.

It is not always possible to add unique IDs to items, such as: B. Lines in a grid or items in a list. In such cases, you can use CSS classes and HTML data attributes to add traceable properties to the elements for easier selection. If you z. For example, if you had two lists of albums on your page, one as a result of user search and another for suggested albums based on the user's previous purchases, you can use a CSS class to distinguish them, even if that class is not for styling the list is used:

If you don't want to use unused CSS classes, you can use HTML data attributes instead and modify the lists as follows:

and:


Debugging UI tests

One of the main reasons UI tests fail is because some element or text cannot be found on the page. Sometimes this happens because you land on the wrong page due to navigation errors or changes to your site's page navigations, or due to verification errors. At other times, it could be due to a missing page or a server error.

Regardless of what is causing the error and whether you can get it in your CI server log or in your desktop test console, a (or something similar) isn't very useful for finding out what went wrong, is it? If the test fails, the only way to fix the error is to run it again and monitor the error. There are a few tricks that may require you to rerun slow UI tests to troubleshoot. One solution to this problem is to take a screenshot when a test fails so we can refer to it later.

There is an interface in Selenium:

/// public interface ITakesScreenshot /// /// /// Screenshot GetScreenshot ();

This interface is implemented by web driver classes and can be used in the following ways:

var screenshot = driver.GetScreenshot (); screenshot.SaveAsFile ("

If a test fails because you are on the wrong page, you can quickly tell from the captured screenshot.

Even taking screenshots is not always enough. For example, you can see the expected item on the page, but the test still fails because it cannot find it. This may be due to the wrong selection, which leads to an unsuccessful item search. Instead of the screenshot (or as a supplement) you can also capture the page source as HTML. There is a proprietary interface (which is implemented by all web drivers):

/// /// string PageSource get;

Just like we did you can implement a method that captures the page source and saves it to a file for later review:

File.WriteAllText ("

You really don't want to capture screenshots and page sources of all the pages you visit and for the tests you passed. Otherwise, you'll have to go through thousands of them if something actually goes wrong. Instead, collect them only when a test fails or when you need more troubleshooting information. To avoid polluting the code with too many try-catch blocks and to avoid code duplication, you should put all element lookups and assertions in a class and enclose them with try-catch and the screenshot and / or capture the page source in the catch block. Here is a bit of code you could use to perform actions on an element:

public void Execute (By by, Action)

The class can be implemented as:

public class Capturer public static string OutputFolder = Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "FailedTests"); private read-only RemoteWebDriver _webDriver; public Capturer (RemoteWebDriver webDriver) _webDriver = webDriver; public void CaptureScreenshot (string filename = null) var camera = (ITakesScreenshot) _webDriver; var screenshot = camera.GetScreenshot (); var screenShotPath = GetOutputFilePath (filename, "png"); screenshot.SaveAsFile (screenShotPath, ImageFormat.Png); public void CapturePageSource (string filename = zero) var filePath = GetOutputFilePath (filename, "html"); File.WriteAllText (filePath, _webDriver.PageSource); private string GetOutputFilePath (string filename, string filename extension) if (! Directory.Exists (OutputFolder)) Directory.CreateDirectory (OutputFolder); var windowTitle = _webDriver.Title; Filename = filename? string.Format ("0 1. 2", windowTitle, DateTime.Now.ToFileTime (), fileExtension) .Replace (':', '.'); var outputPath = Path.Combine (OutputFolder, filename); var pathChars = Path.GetInvalidPathChars (); var stringBuilder = new StringBuilder (output path); foreach (var item in pathChars) stringBuilder.Replace (item, '.'); var screenShotPath = stringBuilder.ToString (); return screenShotPath;

This implementation keeps the screenshot and the HTML source in a folder called FailedTests next to the tests. However, you can change it if you want different behavior.

While I've only shown methods specific to Selenium, similar APIs exist in all of the automation frameworks I know and can be used with no problem.


Conclusion

In this article, we talked about a few tips and tricks for UI testing. We discussed how to avoid a brittle and slow UI test suite by avoiding set delays in your tests. Then we discussed how to avoid brittle selectors and tests by carefully choosing the selectors and how to debug your UI tests when they fail.

Most of the code shown in this article is located in the MvcMusicStore sample repository that we saw in the previous article. It's also worth noting that a lot of code on the MvcMusicStore has been borrowed from the Seleno codebase. So if you want to see a lot of cool tricks then you should check out Seleno. Disclaimer: I am a co-founder of the TestStack organization and a contributor to Seleno.

I hope what we discussed in this article will help you with your UI testing.