2011/05/04

Moving to Selenium 2 on WebDriver, Part No.4

Conditional Waits

Majority part of web UI tests I fixed and keep fixing has the same flaw - there is either no wait for dynamically loaded parts of page or the wait is unconditional. As driver.get() is blocking this is clearly problem of an AJAX calls.

WebDriver provides two APIs for conditional waits - com.thoughtworks.selenium.Wait and org.openqa.selenium.support.ui.Wait.


Class com.thoughtworks.selenium.Wait

Simpler of the two and IMHO better suited for ad-hoc waits is this abstract class. It has only two methods - wait() and until().  You have to implement method until() and it should return true when the condition is met.  

Example of wait for pop-up of given name and switching to it:

public void waitForPopUp(final String windowName, int timeoutMs)
{
   new Wait()
   {
      @Override
      public boolean until()
      {
         try
         {
            driver.switchTo().window(windowName);
            return true;
         }
         catch (SeleniumException ignored) { }
         return false;
      }
   }.wait(String.format("Timed out waiting for %s. Waited %s",windowName, timeoutMs), timeoutMs);
}

Interfaces and classes from org.openqa.selenium.support.ui

More sophisticated approach is represented by interfaces Wait, implemented by WebDriverWait class, and  ExpectedCondition (extending com.google.common.base). Class implementing the ExpectedCondition interface must define method apply() returning true or not-null when a condition is met.

Example of wait expecting JavaScript expression evaluate to true:

int timeoutSeconds = 10;
Wait wait = new WebDriverWait(driver, timeoutSeconds);
JavascriptExecutor js = (JavascriptExecutor)driver;

public boolean waitForJsCondition(final String script, String timeoutMessage)
{
    boolean result = false;

    try
    {
        result = wait.until
        (
            new ExpectedCondition<Boolean>()
            {
               public Boolean apply(WebDriver driver)
               { return (Boolean)js.executeScript("return " +  script); }
            }
        );
    }
    catch (Throwable t)
    { handleWaitTimeout(t,timeoutMessage); }

    return result;
}

As you can see the advantage is that this Wait returns value. You can wait for an element to become visible and return it, thus keeping you code cleaner and more resilient. Even better, expected conditions returning the same type are interchangable. That not only adds to flexibility, it also helps to fight code duplication.

2 comments:

  1. Firstly, thank you for your tutorial on selenium!
    It is very helpful!

    I have a question though, I'm trying to write a waitForAjaxLoaded() in java that corresponiding in fact to a waitForElement(), using your method, but I can't manage to make it work!

    Eclipse has a problem with " new Wait() "
    he is asking me to create a class Wait.
    How can I handle this?
    thank you by advance

    ReplyDelete
  2. In version I currently use, 2.0a6, the Wait class is in selenium-support artifact, pulled in by org.seleniumhq.selenium:selenium:jar, in Maven speech.

    ReplyDelete