Selenium UI testing of AJAX drop-down lists

.NET/ ASP MVC/ UI Testing

Automated UI testing of forms that have AJAX populated drop-down lists can be tricky to test. You might find a race condition where the test is trying to select a value from a drop-down that is dependent on another field. Often it won’t load in time and the test will fail, usually with a StaleElementReferenceException.

A way around it might be to put a pause with say a Thread.Sleep or similar. This is pretty hacky and should be avoided. The way we got around it was using the inbuilt DefaultWait of the WebDriver. Unfortunately the C# version doesn’t yet have the cool FluentlyWait stuff that Java does but if you are using PageObjects, you can use the driver to set up a method similar to the code sample below.

public DefaultWait&lt;IWebDriver&gt; WaitUntil() { var driver = ((RemoteWebDriver) Browser); var wait = new DefaultWait&lt;IWebDriver&gt;(<wbr />driver) { Timeout = TimeSpan.FromSeconds(10), PollingInterval = TimeSpan.FromMilliseconds(100) }; wait.IgnoreExceptionTypes(typeof(NoSuchElementException), typeof(StaleElementReferenceException)); return wait; }

With the DefaultWait you can set a polling interval to keep checking until a certain condition is met and you can set a timeout for how long it will keep polling. You can also tell it to ignore certain exceptions which is handy to get around cases when the field has loaded yet, has changed or is empty, etc.

So if you picture a dropdown, say car makes for example, and another for models. Obviously the models depend on the make. Initially I was targeting and clicking the elements as follows..

var make = Find().Element(By.Id("Make")).FindElements(By.TagName("option")).First(e =&gt; !string.IsNullOrWhiteSpace(e.Text)); make.Click();

var model = Find().Element(By.Id("Model")).FindElements(By.TagName("option")).First(e =&gt; !string.IsNullOrWhiteSpace(e.Text)); model.Click();

This will find the “Make” drop-down by Id, try to find the first option with text that isn’t empty and click it. Next it will locate the “Model” drop-down and try to do the same there. Problem is the model drop-down might not be ready yet. So let’s re-write the second block to wait until it’s loaded properly.

var model = WaitUntil().Until(d =&gt; d.FindElement(By.Id("Model")) .FindElements(By.TagName("option")) .FirstOrDefault(e =&gt; !string.IsNullOrWhiteSpace(e.Text))); model.Click();

Another issue you might come across like I did is that if you insert an element into the drop-down while it’s loading (e.g. “Loading…”) then the condition will match this. It might be better to change it to find the first option that has a value. I.e.

var model = WaitUntil().Until(d =&gt; d.FindElement(By.Id("Model")) .FindElements(By.TagName("option")) .FirstOrDefault(e =&gt; !string.IsNullOrWhiteSpace(e.GetAttribute("value")))); model.Click();