December 21, 2018

Basic Capybara-Gauge: Setting Up Specs and Step Implementations

This is the fifth part of a series of blog posts. Care to go back to the beginning

So far, we have written test specifications to test Dave Haeffner's The-Internet, and we have set up the Gauge-Ruby environment. We managed to get Chrome to run a simple Capybara test, and figured out how to make the test run in Headless Chrome. Our test we created last time...

specs/Navigation/Navigation.spec
# Navigation: Go to The-Internet Login Page

## NAVIGATION: Visit The-Internet
* LOGIN: Visit the Login Page
* LOGIN: Verify the heading is "Login Page"

... For this entry, we will be building upon that, figuring out how to get our Capybara-Gauge framework to execute the following:

Authorization: Validate logging into and out of The-Internet
  • Scenario #1: Authorization: Successfully log into and out of the secure area 
  • Scenario #2: Authorization: Verify alert error is shown when entering a blank user name and password.


Create A Smoke Test

How did we want to test The-Internet? One way to do it:

specs/Authorization/SmokeTests.spec

Authorization: Validate logging into and out of The-Internet

  • LOGIN: Visit the Login Page
  • LOGIN: Verify heading: "Login Page"
Authorization: Successfully log into and out of the secure area
  • LOGIN: Log into the site using username and password: "tomsmith" and "SuperSecretPassword!"
  • SECURE AREA: Verify heading: "Secure Area"
  • SECURE AREA: Log out of the secure area
  • LOGIN: Verify heading: "Login Page"
Authorization: Verify alert error is shown when entering a blank user name and password.
  • LOGIN: Log into the site using username and password: " " and " "
  • LOGIN: Verify alert message: "Your username is invalid"
... I like writing the spec file clearly and concisely. The Spec file can act as a manual testplan, the console output of what passed and what failed, and the HTML Report I can show to the project stakeholders.

Notice that you can't start logging in and out of the secure page or entering invalid data without actually visiting the Login Page (making sure that the page has the proper heading, as a verification step).

I decided, to make it more readable, to explicitly spell out where I am going and what I am verifying. Because both steps, the visiting and the verifying is shared between the two scenarios, I pulled them out to the top, so they will be run before any of the scenarios execute.

Next, let's tackle implementing that first step....

Create Login Step Implementation

Let's look at that first step for specs/Authorization/SmokeTests.spec:
 
## Authorization: Successfully log into and out of the secure area
* LOGIN: Log into the site using username and password: "tomsmith" and "SuperSecretPassword!"

... How would we automate it?

We could create in the step_implementations section a new folder called "Authorization" with a file called SmokeTests.spec, and start building out the automation:
  • Require the Spec Helper
  • Write the test step
  • Find the page elements
If you need any help with Capybara:
If you need any help with Gauge:
 

Require the Spec-Helper

We did a lot of work configuring Chrome to work with Gauge. Let's make sure to require that  step_implementations/login_spec.rb uses this data:
  • require_relative 'spec_helper'

Write Test Steps For Login

 Gauge has two main parts to its framework:
  • Specs: The human readable test cases and scenarios that will be included in the reports. Test steps are indicated as bullet points.
  • Step Implementations: Each bullet point in the spec needs a corresponding step component that lists what needs to be done. 
The Specs are powerful because they can be written in a language that business analysts can understand. If you write each spec in such a way that it can be turned into a reusable component in the step implementation library, it can be even more powerful.

Let's take the first test scenario, for instance. We could write it like:

## Authorization: Successfully log into and out of the secure area
* LOGIN: Log into the site using username and password: tomsmith and SuperSecretPassword!


... But what happens if you want to enter other usernames or passwords? We would need a new spec for each username and password combination. Instead, we can make the step we will be creating to be data driven, indicating that the username and password are variables by putting quotes around the value.

## Authorization: Successfully log into and out of the secure area
* LOGIN: Log into the site using username and password: "tomsmith" and "SuperSecretPassword!"


We can then write the corresponding step where the value "tomsmith" is passed into a variable, "username", and "SuperSecretPassword!" is passed into the variable "password":

step_implementations/login_spec.rb
step 'LOGIN: Log into the site using username and password: <username> and <password>' do |username, password|
  ## Enter data here
end

Find The Login Page Elements


Take a look at the elements on the Login Page at https://the-internet.herokuapp.com/login

You have a Login Header, a Username Field, a Password Field, and a Login Button. And if you hit the Login button without filling in the username or password, you get an alert message. 

Capybara can automatically find web elements by label. In my experience, throughout the life of an app, Labels constantly change. Labels get longer, shorter, or get modified. "Username" could be changed to "User" in the next iteration. Right now, the Login button is labelled "Login", but it could also changes.

Capybara can automatically find web elements by css selector or id. Select the LOGIN button without entering text, and you see the alert message "Your username is invalid". With Chrome, if you Right-Click and select "Inspect", in Chrome Developer Tools you can see that the data alert is contained in a DIV with the id "flash-messages". This ID can also change.

If we store the locators on the top of the page in a Ruby constant, we won't have to start digging through the code the next time they change.

step_implementations/login_spec.rb
  • LOGIN_HEADER = 'h2'
  • USERNAME_FIELD = 'Username'
  • PASSWORD_FIELD = 'Password'
  • ALERT_MESSAGES = 'flash-messages'
  • LOGIN_BUTTON = 'Login'

 

Create The Login Step


Capybara has two methods we can use: fill_in and click_button. But what happens if we receive errors? How would we be able to know how the fields were actually filled out?

Gauge gives us a method called "write_message", where we can send messages to the HTML report.I love this feature! Never trust Green. Make sure you review the report produced after a successful run to make sure that your test framework is doing what you expect it to do.

Using these methods, combining it with the page elements created, we get for login_spec.rb:

step 'LOGIN: Log into the site using username and password: <username> and <password>' do |username, password|
  Gauge.write_message("\t* Entering Username: '#{username}'")
  fill_in(USERNAME_FIELD, with: username)
  Gauge.write_message("\t* Entering Password: '#{password}'")
  fill_in(PASSWORD_FIELD, with: password)
  click_button(LOGIN_BUTTON)
end

Notice that when we were creating login_spec.rb, we really didn't generate that much code. Capybara does most of the heavy lifting when it comes to waiting for page elements to load, sending keys, and pressing buttons.

Normally, I would encapsulate each action I can do on the page in its own public method, contained in a page object. With Gauge, it already has each action set up in each step. The Gauge.org people prefer it this way, writing blog posts about how they dislike the Page Object Pattern ("The Page Objects anti pattern"). ... When in Rome! ...

Write Test Steps for Secure Area

 Next, we are going to be implementing the Secure area steps...
  • SECURE AREA: Verify heading: "Secure Area"
  • SECURE AREA: Log out of the secure area
Let's create a new file in the step_implementations section... 

secure_area_spec.rb
 require_relative 'spec_helper'  
   
 SECURE_HEADER = 'h2'  
 LOGOUT_BUTTON = '#content > div > a > i'  
   
 step 'SECURE AREA: Verify heading: <page_header>' do |page_header|  
  expect(SECURE_HEADER).to have_content(page_header)  
 end  
   
 step 'SECURE AREA: Log out of the secure area' do  
  find(LOGOUT_BUTTON).click  
 end  

Why did we write the validation step verifying the heading with the variable "page_header"?  Labels and headers change all the time. They should not be hardcoded. If the "Secure Area" page was given a new heading, such as "Secure Section", I only want to change the test specification. I don't want to dig into the code itself.

Likewise, as before, I lifted out the locator of the Logout button, placing that into a Ruby constant.

Two things I want to point out:

The Expect Method:
  • Are you definitely expecting something to show up immediately, within the next ten seconds? Using expect is fine. 
  • Are you dealing with a slow loading component or a slow loading page? Rewrite the code so it will wait for whatever you designated in the spec_helper.rb file as the TIMEOUT: (SECURE_HEADER).should have_content(page_header)
The Find Method:
  • Sometimes you want more control over where you are searching for a certain element. Fill_in and click_button search the entire page by default. You can choose to find the element first, then use the click method to click on the element... if, of course, it is clickable. 

 

Make Sure To Reuse Steps!

How do we make sure that we are successfully returned to the Login page? Check the page header!

... Wait, don't we already have a step created for this?
  •  LOGIN: Verify heading: "Login Page"
Yes! We created it for the Navigation test, and reused it for setting up this test scenario.

All we need to do is add that line to the scenario, and we are all set!

Does Everything Still Run?

Run all Authorization/SmokeTests.spec test cases in verbose mode to make sure everything still works and see the output of each step:
  •  bundle exec gauge run specs/Authorization -v
Or we can run all the tests at once:
  •  bundle exec gauge run specs -v

Verifying the Alert Errors

Next, we have the following scenario to automate:

Authorization: Verify alert error is shown when entering a blank user name and password.
  • LOGIN: Log into the site using username and password: " " and " "
  • LOGIN: Verify alert message: "Your username is invalid"
... That first step, it already exists! We can focus on finding the locator for the area where messages appear, and expecting that message to appear in that section.

If we were to trigger an error by entering the LOGIN button without any data, and in Google Chrome we Inspect that field, we can see that the area is called "flash-messages".

The code we can add to login_spec.rb to handle this test:
 ALERT_MESSAGES = 'flash-messages'  
   
 step 'LOGIN: Verify alert message: <alert_message>' do |alert_message|  
  expect(ALERT_MESSAGES).to have_content(alert_message)  
 end  

This gives us our final version:

step_implementations/login_spec.rb:
 require_relative 'spec_helper'  
   
 APP_URL = 'https://the-internet.herokuapp.com/login'  
 LOGIN_HEADER = 'h2'  
 USERNAME_FIELD = 'Username'  
 PASSWORD_FIELD = 'Password'  
 ALERT_MESSAGES = 'flash-messages'  
 LOGIN_BUTTON = 'Login'  
   
 step 'LOGIN: Visit the Login Page' do  
  Gauge.write_message("Navigating to: '#{APP_URL}'")  
  visit(APP_URL)  
 end  
   
 step 'LOGIN: Verify heading: <page_header>' do |page_header|  
  expect(LOGIN_HEADER).to have_content(page_header)  
 end  
   
 step 'LOGIN: Log into the site using username and password: <username> and <password>' do |username, password|  
  Gauge.write_message("\t* Entering Username: '#{username}'")  
  fill_in(USERNAME_FIELD, with: username)  
  Gauge.write_message("\t* Entering Password: '#{password}'")  
  fill_in(PASSWORD_FIELD, with: password)  
  click_button(LOGIN_BUTTON)  
 end  
   
 step 'LOGIN: Verify alert message: <alert_message>' do |alert_message|  
  expect(ALERT_MESSAGES).to have_content(alert_message)  
 end  

Running the test we can see that everything passed! Check the tests in the Navigation + Authorization section:
  •  Mac Terminal: bundle exec gauge run specs -v
How does it work in normal Chrome? In env/default/ruby.properties:
  • HEADLESS = false
How does it work in headless Chrome? In env/default/ruby.properties:
  • HEADLESS = true  
Did you install the HTML-REPORT Plugin?
... If you did, open in a browser the reports/html-report/index.html to view it. 

Did you install the XML-REPORT Plugin?
... If you did, open in a browser the reports/xml-report/result.xml to view it.

Did you install the JSON-REPORT Plugin?
  • gauge install json-report
... If you did, open in a browser the reports/json-report/result.json to view it.

Unsure of what your setup is?
  • gauge version

 

View Reports Produced


Want to see some sample reports generated by running the finished product? Take a look at the project directory on GitHub of my capybara-gauge project:


Next, let's make things interesting, editing the spec_helper.rb file to add Chrome and Chromedriver logging... 

Happy Testing!


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

Twitter | YouTubeLinkedIn | Articles