Friday 10 February 2012

Use Explicit Waits in Selenium WebDriver Correctly

I encountered a very strange problem with selenium webdriver recently. I was using selenium to find a piece of text in a <p> element on a relative complexed webpage. The problem was sometimes selenium is able to find the text, and sometimes not. After some research, I found the following piece of information in selenium documentation:

Dependent on several factors, including the OS/Browser combination, WebDriver may or may not wait for the page to load. 

The documentation also suggests that WebDriverWait should be used for more consistent behaviour. And here is how to do it "officially":


            IWebDriver driver = new FirefoxDriver();
            driver.Url = "http://somedomain/url_that_delays_loading";
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
            IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
            {
                return d.FindElement(By.Id("someDynamicElement"));
            });


But when I tried it, it still sometimes throws NoSuchElementException even before specified timeout period. So I kept on reading, and according to the documentation:

This waits up to 10 seconds before throwing a TimeoutException or if it finds the element will return it in 0 - 10 seconds. WebDriverWait by default calls the ExpectedCondition every 500 milliseconds until it returns successfully. A successful return is for ExpectedCondition type is Boolean return true or not null return value for all other ExpectedCondition types.

That explains it. The anonymous method above neither return false nor null when it can't find the element. Therefore TimeoutException will not happen if it can't find the specified element. So the "official" example does not actually work.

Fortunately, only a simple change is required to make it work again;

            IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
            {
                try
                {
                    return d.FindElement(By.Id("someDynamicElement"));
                }
                catch
                {
                    return null;
                }
            });

No comments:

Post a Comment