Skip to main content

_ruby

Example 1

Let's use from the-internet which load content dynamically (link). On it there is a Start button that when clicked will trigger a loading bar to appear for 5 seconds. After that time it will disappear and a new element with the text 'Hello World!' will appear.

We'll start by requiring our dependent libraries (selenium-webriver to drive the browser and rspec/expectations & RSpec::Matchers to perform our assertions) and wiring up some setup, teardown, and run methods to handle our test configuration.

# filename: dynamic_loading.rb

require 'selenium-webdriver'
require 'rspec/expectations'
include RSpec::Matchers

def setup
@driver = Selenium::WebDriver.for :firefox
end

def teardown
@driver.quit
end

def run
setup
yield
teardown
end

Now we can create our first test. In it, we'll visit the page, click the start button, wait for the finish text to appear, and assert that it appeared.

run do
@driver.get 'http://the-internet.herokuapp.com/dynamic_loading/2'
@driver.find_element(css: '#start button').click
Selenium::WebDriver::Wait.new(timeout: 6).until { @driver.find_element(id: 'finish').displayed? }
expect(@driver.find_element(id: 'finish').displayed?).to eql true
end

In the code above we are using an explicit wait with a timeout of 6 seconds (e.g. Selenium::WebDriver::Wait.new(timeout: 6).until), looking for the element with the finish text (e.g., @driver.find_element(id: 'finish'), and seeing if it's displayed (e.g., .displayed?).

If we set the timeout too low (e.g. timeout: 2) Selenium won't wait long enough for the loading bar to finish, which will trigger the timeout threshold and throw an exception (e.g. until': timed out after 2 seconds (Selenium::WebDriver::Error::TimeOutError)).

And if we didn't use an explicit wait at all, the test would have errored because Selenium would have tried to check for the finish text before it displayed and return a NoSuchElement exception (e.g., Unable to locate element: {"method":"css selector","selector":"#finish"} (Selenium::WebDriver::Error::NoSuchElementError)).

For applications that already have the target element on the page but have it hidden, this approach will work as well (which we can see by pointing our test at the other dynamic loading example).

run do
@driver.get 'http://the-internet.herokuapp.com/dynamic_loading/1'
@driver.find_element(css: '#start button').click
Selenium::WebDriver::Wait.new(timeout: 6).until { @driver.find_element(id: 'finish').displayed? }
expect(@driver.find_element(id: 'finish').displayed?).to eql true
end

Example 2

Explicit waits are a useful tool to have in your toolbelt since they enable you to finely tune your tests and avoid static sleeps and arbitrary timeouts. But rather than litter your test code with the verbose Selenium incantation, it's best to pull this code out into a method that accepts a timeout argument and the action you want to wait for as a block.

You can then clean up our test code and reuse this method whenever an explicit wait is needed.

def wait_for(seconds = 6)
Selenium::WebDriver::Wait.new(:timeout => seconds).until { yield }
end

run do
@driver.get 'http://the-internet.herokuapp.com/dynamic_loading/1'
@driver.find_element(css: '#start button').click
wait_for { @driver.find_element(id: 'finish').displayed? }
expect(@driver.find_element(id: 'finish').displayed?).to eql true
end

Expected Behavior

When you save the file and run it (e.g., ruby dynamic_loading.rb from the command-line) here is what will happen:

  • Open the browser
  • Visit the page
  • Click the Start button
  • Wait for the loading bar to disappear and display the finish text
  • Assert that the finish text element is displayed
  • Close the browser

Summary

Hopefully this tip will help you build more efficient and resilient tests when faced with dynamic pages that are constructed in various ways.

Happy Testing!