November 7, 2021

The Cheezy Internet: Writing Ruby + Watir tests for The-Internet

This is third in a series of blog posts. Care to go back to the beginning?

Last blog entry, based on Jeff "Cheezy" Morgan's LeanPub book, "Cucumbers and Cheese: A Tester's Workshop", we set up our local machine, wrote and executed our first Watir program. 

Based on his book, we are going to attempt to sketch out and write our first test against Dave Haeffner's sample login page on The-Internet at https://the-internet.herokuapp.com/login.

The-Internet / Login Page


Drafting a Test 

A sample test for The-Internet / Login could be:
  • Go to https://the-internet.herokuapp.com/login
  • Enter tomsmith into the username textbox.
  • Enter SuperSecretPassword! into the password textbox. 
  • Press the Login button. 
  • If the Secure Area page does not show "'Welcome to the Secure Area", fail the test.


Finding Locators for Web Elements

How can we interact with the web elements on the site, such as the username and password textboxes, and the Login button? 
  • Open a Chrome browser. 
  • Click on a web element, then right click, selecting "Inspect". 
  • Try to find a tag in the code, such as "id". 
  • If you do that for username you will see: input type="text" name="username" id="username"

Writing a Watir Test


According to Jeff Morgan's Cucumbers and Cheese, we can write an initial Watir script like so:

second-script.rb
require 'watir'
browser = Watir::Browser.new :chrome

browser.goto 'https://the-internet.herokuapp.com/login'

browser.text_field(id: 'username').set('tomsmith')
browser.text_field(id: 'password').set('SuperSecretPassword!')
browser.button(value: 'Login').click

if not browser.text.include? 'Welcome to the Secure Area'
    fail
end


To execute the script, in PowerShell, go into the directory where you saved the script and:
  • ruby second-script.rb

Adding Error Handling


Running the script, a browser will open up, any you will be logged in, but it will PASS in silence.

How do you know it worked? What happens if you change that to check if it says "Welcome to the ThunderDome"?

if not browser.text.include? 'Welcome to Thunderdome'
    fail
end


If we run the code, it now throws the error:

Traceback (most recent call last):
first_script.rb:11:in `<main>': unhandled exception


Hrm. That is inelegant. 

Jeff Morgan suggests we can rewrite this error using Ruby, adding in error handling:

fail 'Text did not match expected value' unless browser.text.include? 'Welcome to the Secure Area'


Refactoring Code Into Methods

"In Ruby, as in many other Object Oriented programming languages, we have named reusable
modules of code called methods55. We have already discussed calling methods but it is also possible
to create your own. Indeed, as we’ll see later, a large part of keeping any Object Oriented automated
testing code (or any code) D.R.Y. involves creating our own classes and methods, as if we were
creating our own little testing language. Method definitions in Ruby have the following form, where
'def' is short for 'define' - Jeff Morgan, "Cucumbers and Cheese".

Let's say we want to declare the browser in a method, we can make "browser" an instance variable.

"Instance variables are available anywhere in the class to which they belong. It made
sense for us in our example to make the browser variable accessible to every method in
our class, because any method in this class would likely need it. And it is messy and noisy
to pass it around to any method that needs it, especially if several methods in a class do
need it" - Jeff Morgan, Cucumbers and Cheese: A Tester's Journey

def goto_login_page
  @browser = Watir::Browser.new :chrome
  @browser.goto 'https://the-internet.herokuapp.com/login'
end

This way, the main body could simply say "goto_login_page", making it much more readable. 

Rewriting what we already have into methods, searching for text that is not actually on the page, it would look like:

third_script.rb
require 'watir'

def goto_login_page
  @browser = Watir::Browser.new :chrome
  @browser.goto 'https://the-internet.herokuapp.com/login'
end

def login_as(username, password)
  @browser.text_field(id: 'username').set(username)
  @browser.text_field(id: 'password').set(password)
  @browser.button(value: 'Login').click
end

def verify_page_contains(text)
  fail "Expected text not found: #{text}" unless @browser.text.include? text
end

def close_the_browser
  @browser.close
end

goto_login_page
login_as('tomsmith', 'SuperSecretPassword!')
verify_page_contains('Welcome to the Thunderdome')
close_the_browser



"Now our testing script is short, sweet, and all about WHAT we are testing. Not only have
we hidden the details of HOW we do this work, we have made that reusable code accessible to any other test module we need [...]" - Jeff Morgan 
Note that now:
  • The script is more readable.
  • Login information is now passed into the login_as method.
  • The verify_page_contains method can search for any text we pass into it. 
  • If we cannot find the text on the Secure area, the failure message will now contain whatever text we were trying to find. 
Running the script, it would output:

third_script.rb:15:in `verify_page_contains': Expected text not found: Welcome to the Thunderdome (RuntimeError)

If you would like even a more detailed example, get a copy of "Cucumbers and Cheese: A Testers Workshop", where Jeff Morgan tests against http://puppies.herokuapp.com/ 

For the next blog entry, we will be scaffolding a project composing these basic building blocks we have created into tests using Cucumber / Gherkin. 



Happy Testing!

-T.J. Maher
Sr. QA Engineer, Software Engineer in Test
Meetup Organizer, Ministry of Testing - Boston

Twitter | YouTubeLinkedIn | Articles

No comments: