_ruby
Example
Let's start with an example -- testing a login form on a website (e.g., with this example on the-internet).
First, we are going to create a file to store the test inputs and expected outputs in. In this case, a CSV (comma-separated value) file called user_data.csv
, like so:
account_type,username,password,notification_message
bad_password,tomsmith,badPassword,Your password is invalid!
bad_username,badUsername,SuperSecretPassword!,Your username is invalid!
standard_user,tomsmith,SuperSecretPassword!,You logged into a secure area!
Next let's create our test file, require our requisite libraries (e.g., selenium-webdriver
to control the browser, rspec/expectations
& RSpec::Matchers
for our assertion, and csv
to import the data from our CSV file) and add some simple setup
, teardown
, and run
methods.
# filename: data_driven.rb
require 'selenium-webdriver'
require 'rspec/expectations'
require 'csv'
include RSpec::Matchers
def setup
@driver = Selenium::WebDriver.for :firefox
end
def teardown
@driver.quit
end
def run
setup
yield
teardown
end
Now let's wire up our CSV parsing.
def user_data
user_data = CSV.read Dir.pwd + '/user_data.csv'
descriptor = user_data.shift
descriptor = descriptor.map { |key| key.to_sym }
user_data.map { |user| Hash[ descriptor.zip(user) ] }
end
In user_data
we read the CSV file in and grab the first row in it with .shift
. We store this row in a variable called descriptor
and convert each value into a symbol. We then iterate over the rest of the CSV data and create a Hash object for each row of user data. In each Hash we are binding the relevant descriptor to each piece of user data (through the use of .zip
). This enables us to reference each of the user values by looking them up in the Hash by their descriptor symbol.
Next we'll need a helper method that will responsibly obtain the notification text from the page. It will need to both wait for the text to appear and clean up the resulting text, so it doesn't include any extraneous characters.
def notification_text
wait = Selenium::WebDriver::Wait.new(timeout: 5)
wait.until { @driver.find_element(class: 'flash').displayed? }
@driver.find_element(class: 'flash').text.delete('^a-zA-z !.')
end
We use the stock Selenium Wait function (e.g., an explicit wait) to continuously perform an action until either the timeout is reached or the action is true (whichever comes first). We then use it to see if the notification text is displayed on the page. If it is, then we grab the text and clean it up with a regular expression that deletes non-letter characters while preserving !
and .
(which are expected in the output we're testing for).
Now we're ready to wire up our test. In it, we will iterate through each of the user entries in the CSV file and use their data to both complete a login action and verify the notification message.
user_data.each do |user|
run do
@driver.get 'http://the-internet.herokuapp.com/login'
@driver.find_element(id: 'username').send_keys user[:username]
@driver.find_element(id: 'password').send_keys user[:password]
@driver.find_element(id: 'login').submit
begin
expect(notification_text).to eql user[:notification_message]
rescue Exception => error
puts error.message
end
end
end
We wrap the notification message assertion in a rescue
block so that when an exception occurs the test will continue on with the next piece of user data after outputting the failure message to the command prompt.
Expected Behavior
If you save this file and run it (e.g., ruby data_driven.rb
from the command-line) the script will parse the CSV file and perform the following for each entry:
- Open the browser
- Load the login page
- Submit the login form with user data
- Grab the notification message and assert it against the expected value
- Close the browser
Summary
By storing data in a central repository to use for your tests and using Data-driven testing, you can simplify running repeated tests with various outputs.
Happy Testing!