Skip to main content

_ruby

Example

In this example we are going to take the following sample code and upgrade it with the use of a Page Object.

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

def setup
@driver = Selenium::WebDriver.for :firefox
@base_url = 'http://the-internet.herokuapp.com/dynamic_loading/2'
end

def teardown
@driver.quit
end

def run
setup
yield
teardown
end

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

run do
@driver.get @base_url
@driver.find_element(css: '#start button').click
wait_for { @driver.find_element(css: '#finish').displayed? }
expect(@driver.find_element(css: '#finish').text).to eql('Hello World!')
end

The first order of business is pretty simple. We keep our setup, teardown, and run actions mostly the same. But we change the base_url from an instance variable to an environment variable. Doing this will enable us to access it from anywhere in our test suite.

While the usage of environment variables can be a slippery slope, this is an ideal candidate for it since it effects the overall behavior of the suite.

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

def setup
@driver = Selenium::WebDriver.for :firefox
ENV['base_url'] = 'http://the-internet.herokuapp.com/dynamic_loading/2'
end

def teardown
@driver.quit
end

def run
setup
yield
teardown
end

Next we create a Page Object for the Dynamic Loading page by using a standard Ruby class and add some relevant bits to it.

At the top of it we pull out the CSS locators used in our test steps and put them into helpfully named constants to use instead. And instead of using the two part 'how', 'what' approach, we are using a hash to store the locator type and its value.

Next we use attr_reader to create a place to store the Selenium WebDriver instance for use throughout the class.

The class expects an argument (e.g. the Selenium WebDriver instance) which is received through the initialize method. Inside the initialize method we take care of passing the Selenium object into the attr_reader object along with visiting the page and verifying that we are in the correct place. All these things will execute in order when this class is instantiated.

We then break out each of the test steps into methods that execute the behavior specific to the page while also swapping out the hard-coded CSS locators for our new CSS locator constant variables.

At the bottom of the class we have private helper methods. These methods aren't necessarily specific to the page this class represents, but they are useful for it to function. So we want to access them within the class, but make it so that they're not available outside the class. Using the private classification gets us this behavior nicely.

And lastly, we update the displayed? private method to take a single argument for a 'locator' object (e.g. one of our CSS locator constant variables).

class DynamicLoading

START_BUTTON = { css: '#start button' }
FINISH_BUTTON = { css: '#finish' }

attr_reader :driver

def initialize(driver)
@driver = driver
visit
verify_page
end

def visit
driver.get ENV['base_url']
end

def start
driver.find_element(START_BUTTON).click
end

def finish_present?
wait_for { @driver.find_element(FINISH_BUTTON).displayed? }
end

def finish_text
driver.find_element(FINISH_BUTTON).text
end

private

def verify_page
expect(driver.title).to eql('The Internet')
end

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

end

With our new Page Object in hand, our run action cleans up considerably. Making it more succinct and readable.

run do
dynamic_loading = DynamicLoading.new(@driver)
dynamic_loading.start
dynamic_loading.finish_present?
expect(dynamic_loading.finish_text).to eql('Hello World!')
end

It's worth noting that while we are peforming an assertion in the Page Object in our verify_page action assertions should only be performed in your test scripts (just like the run action above). Using a verify_page action is just a helpful exception to the rule.

Expected Behavior

  • Load Dynamic Loading page on The Internet
  • Click on start
  • Wait for the finish message to appear
  • Grab the text from it
  • Assert that the text we want is within it

Summary

Hopefully this tip has helped you wade into the waters of Page Objects in Selenium.

Stay tuned for a future tip where we implement a Base Page Object class to abstract things even further and roll our own Domain Specific Language (DSL) for our test suite.

Until then, Happy Testing!