Selenium Tips: Infinite Loops Take Forever

In Selenium, it's a common for the tool to get ahead of the browser. Thus, the element we want to click on does not exist yet - but it will!

We’ll start with a sample html page that pauses four seconds before injecting a link onto the page. In practice, this would probably be a wait for javascript to make a REST API call and push the results into the document.

Our naive test for the link in simple ruby:

element = @driver.find_element :xpath, "//a[text() = 'Sauce Labs']"
assert_equal('https://www.saucelabs.com/', element.attribute('href'));

It only fails because the link does not exist on the page (yet). The simple fix, in the terrible_example, is to add a sleep.

Don’t do that.

Our next attempt will be infinite_loop.rb, which uses a while loop to wait for the link text to appear on the page:

#And now we'll do the text for the HREF to appear
page_source = @driver.page_source
includes_sauce = page_source.include? "Sauce Labs";
while not (includes_sauce)
 sleep(0.01);
 page_source = @driver.page_source;
 includes_sauce = page_source.include? "Sauce Labs";
end

This works just fine, until we have some sort of error where the text never appears - the exact kind of bug our software is looking for. Now when that happens we’ll be stuck in an infinite loop. That’s no good.

We’ll write two functions to do this. First, we can pull body_contains logic into a method:

  def body_contains(driver,expected_content)
    source = driver.page_source;
    puts "expected ( " +  expected_content + " ) \n\n";
    puts "source ( " +  source + " ) \n\n";
    puts (source.include? expected_content).to_s();
    return source.include? expected_content;
  end

Then we can write the containher function:

  #Adapted with permission from other Sauce Labs Examples
  def contains_with_timeout(driver, expected, timeout=60)
   start = Time.now();
   found = false;
   puts start.to_s();
   puts Time.now().to_s();

   while (Time.now()-start<timeout) and not found
       if body_contains(driver, expected)
           found = true;
       else
         sleep(0.25);
       end
   end
   return found;
  end

It won’t take much to adapt this so that contains_with_timeout is actually anything_with_timeout, where the comparison method is passed in during the function call.

For now, here’s the code for our wait:

contains_with_timeout(@driver,"Sauce Labs");

Read the code yourself in github as timer.rb. The directory in github contains all three full running example and the sample HTML page. Experiment with changing the timeouts and delays to see the errors fire.

That’s right, I said four.

WAIT, THERE'S MORE!

Selenium webdriver includes Explicit Wait, the fourth example in our set.

That code is as simple as:

#And now we'll do the text for the HREF to appear
wait = Selenium::WebDriver::Wait.new(:timeout => 15) # seconds
begin
   element = wait.until { @driver.find_element(:id => "truth") }
ensure

end
assert_equal('https://www.saucelabs.com/', element.attribute('href'));

So go ahead, use the webdriver explicit wait.

We just thought you might appreciate knowing how to do it yourself.

Written by

Matthew Heusser

Topics

Selenium