Thursday, August 11, 2011

Getting started with automation

We just began on a new project. The automation for the project is to be done using ruby, rspec and watir. There are 2 QAs on the team, me and on of my colleague, who has not worked on ruby, rspec or watir. Hence I paired up with him and we created this small tutorial for any newbie to get started with automation. This is a step by step approach to build a suite of automated tests.

This tutorial will help you:

1. Write basic watir tests in ruby
2. Create a basic automation structure using rspec
3. Re-factor the code to use page object model

Sample Test Cases chosen for automation:

1. Login to http://etsy.com with correct credentials and verify user logged in successfully
2. Login to http://etsy.com with incorrect credentials and verify user log-in fails

Setup:

0. Get a machine Linux, Windows or Mac (I am using ubuntu 11.04)
1. Install ruby (I'm using ruby 1.8.7)
2. Install rubygems (I'm using 1.4.2)
3. Install watir
  • On Linux do 'gem install watir-webdriver'
  • On windows do 'gem install watir'
4. Install RubyMine IDE (I'm using the 30 day eval. version, You may choose to use any text editor instead)

Now we are all set to write our first test.

Writing the first test

1. Create a file login_to_etsy.rb
2. Copy the following code. The comments below explain the code

//This class requires the following gems
require "rubygems"
require 'watir-webdriver'

class LoginToEtsy

  //login test method
  def login_test

     //Create a new watir browser instance
     b = Watir::Browser::new :firefox (#On windows you can use Watir::IE.new)

     //Navigate to the etsy.com website
     b.goto 'http://www.etsy.com'


     //Click on a 'a' tag with 'id' equals 'sign-in'
     b.a(:id => 'sign-in').click

     //Enter username and password in the respective boxes
     b.text_field(:id=>'username-existing').set'twtest'
     b.text_field(:id=>'password-existing').set 'test123'
     
     //Click on the sign-in button
     b.button(:value=>'Sign In').click

     //End of test, quit the browser
     b.quit
  end
end

//To execute the above login_test method, create a instance of the class and call the method
LoginToEtsy.new.login_test

To run the test just run the following command from your shell

ruby login_to_etsy.rb

Here we used methods like 'set' and 'click' and elements like 'button', 'text_field', 'a' that are provided by the watir-webdriver api. You can find more here http://jarib.github.com/watir-webdriver/doc

That was our first executable test.

3. The above test method opens the page, opens the login modal, enters username, password and clicks sign-in. However its not complete without a verification step. Let's now make sure that the user is signed in.

In the require list add 'rspec' gem. We will use assertions provided by 'rspec'

require 'rspec'

After clicking the sign-in button add this verification step

#Verifying that user is signed in. The '.should' is a rspec assertion
(b.ul(:id=>'meta-nav').text.include?'Sign Out').should == true

That completes our first test.

4. To run the test go the folder where the file 'login_to_etsy.rb' is present and run the following command

ruby login_to_etsy.rb

This will open firefox browser, execute the test steps and close the browser at the end of the test.

Here, we have used rspec for assertion but there are a lot of things you can do using rspec. Rspec is a Behavior Driven Development (BDD) framework for Ruby programming language.

Lets do it the Rspec way

1. Create a spec file login_spec.rb

2. Copy the following code


//Require the following gems
require "rubygems"
require 'watir-webdriver'
require "rspec"

//The 'describe' method is used to describe a particular behavior or functionality of a system. E.g. the login behavior
describe "Login Behavior" do

     //The 'it' method is an example of the behavior. e.g. successful login, unsuccessful login
     it "should successfully login with correct credentials" do
          b = Watir::Browser::new :firefox
          b.goto 'http://www.etsy.com'


          b.a(:id => 'sign-in').click


          b.text_field(:id=>'username-existing').set 'twtest'
          b.text_field(:id=>'password-existing').set 'test123'
          b.button(:value=>'Sign In').click
          
          (b.ul(:id=>'meta-nav').text.include?'Sign Out').should==true
          b.quit
     end
end

Above, the rest of the test remains same. We can add another example 'it' block inside the describe block which will test another example of the same behavior/functionality. E.g. below

it "should fail when incorrect credentials are provided" do
     b = Watir::Browser::new :firefox
     b.goto 'http://www.etsy.com'
     
     b.a(:id => 'sign-in').click
     
     b.text_field(:id=>'username-existing').set 'incorrect username'
     b.text_field(:id=>'password-existing').set 'incorrect password'
     b.button(:value=>'Sign In').click


     #Note that this is verification step is different from the one used above.
     #Here we are verifying that the login fails with invalid credentials
     b.span(:id=>'username-existing-error').exists?.should==true


     b.quit
end

So now the test looks like

describe "Login Behavior" do

     it "should successfully login with correct credentials" do
          #code code code
     end

     it "should fail when incorrect credentials are provided" do 
          #code code code
     end
end

3. To run the test, go to the folder where the file 'login_spec.rb' is present and run the following command

rspec login_spec.rb

This will run both the examples from the login behavior.

4. Using Before and After methods in rspec

In both the examples above we start the browser and navigate to the home page before the steps for test are executed and we close the browser at the end of test. Since this common for both the examples, they can be extracted in a before block and after like below:

require "rubygems"
require 'watir-webdriver'
require "rspec"

describe "Login Behavior" do


     #The before block here will be executed before each of the example
     #@b is an instance variable and can be used across multiple methods in the same class
     before(:each) do
          @b = Watir::Browser::new :firefox
          @b.goto 'http://www.etsy.com
     end

     it "should successfully login" do
	 @b.a(:id => 'sign-in').click
          .....
          .....
	 (@b.ul(:id=>'meta-nav').text.include?'Sign Out').should == true
     end

     it "should not successfully login" do
          @b.a(:id => 'sign-in').click
          .....
          .....
          @b.span(:id=>'username-existing-error').exists?.should==true
     end

     #The code in the after block will be executed after each of the example
     after(:each)do
          @b.close
          @b.quit
     end
end


You can also use before(:all) and after(:all) which will be executed respectively once before and after all the examples in the spec file.

If we want to execute a common 'before' and 'after' block before and after all the examples in your suite (containing multiple spec files) we can extract these in a common spec_helper.rb and use this in all the spec.

spec_helper will look like below:

require 'rubygems'
require 'rspec'
require 'watir-webdriver'

Spec::Runner.configure do |config|

config.before(:each) {
     #$b indicate here that it is a global variable and can be accessed from any class
     $b = Watir::Browser::new :firefox
     $b.goto 'http://www.etsy.com'
}

config.after(:each) {     
     $b.close
     $b.quit
}

In the spec files we can now remove the before and after block and will have to include the following
require 'spec_helper.rb'

Re-factor the tests to use page object model

The page model is a pattern that maps a UI page of the application under test to a class. The fields on the page are captured as the state or variables in the class and the behavior of the page is captured as methods in the class. These methods and state variables can then be used by the test. The model can be represented as below

HTML Page ==>> Page Class ==>> Test

However there can be another abstraction layer of user actions that define a user action and can span multiple pages. Something like below:

HTML Page ==>> Page Class ==> Actions ==>> Test

Lets re-factor our tests to create a automation suite to follow page-object pattern

1. Look for the code that is common across tests and belongs to one page on the application
In our case the following three lines belong to the login modal page


b.text_field(:id=>'username-existing').set 'incorrect username'
b.text_field(:id=>'password-existing').set 'incorrect password'
b.button(:value=>'Sign In').click

2. Now create a 'pages' folder, and under this folder create a login_page.rb. Copy the following code in this class file

require 'rubygems'
require 'watir-webdriver'

class LoginPage
     def login username, password
          $b.text_field(:id=>'username-existing').set username
          $b.text_field(:id=>'password-existing').set password
          $b.button(:value=>'Sign In').click
     end
end

The above method assumes that the user is on the login modal and performs the login action. The action to open this modal (viz. clicking the Sign-in link is a part of another page) and hence is not a part of this page class.

This model is easy to create. The page class should contain only what is visible on the page and only what functionality can be performed on that page.

3. Change the tests to use the methods in the pages. The test will finally look like below:

require "rubygems"
require 'watir-webdriver'
require "rspec"
require './pages/login_page.rb' #Including the page required in this test
require 'spec_helper.rb' #Including the spec helper which contains the before and after methods

describe "Login Behavior" do
     it "should successfully login" do
          $b.a(:id => 'sign-in').click
          LoginPage.new.login 'twtest', 'test123'
          ($b.ul(:id=>'meta-nav').text.include?'Sign Out').should == true
     end

     it "should not successfully login" do
          $b.a(:id => 'sign-in').click
          LoginPage.new.login 'asfd', 'test123'
          $b.span(:id=>'username-existing-error').exists?.should==true
     end
end

Practice more and have fun. That's all for now.

6 comments:

  1. Cool, but you could put all the codes inside styles it would improve the user experience. http://developertips.blogspot.com/2007/08/syntaxhighlighter-on-blogger.html

    ReplyDelete
  2. Another good (little refactoring) thing could be:

    def successfully
    ($b.ul(:id=>'meta-nav').text.include?'Sign Out').should
    end

    Among the others required operations, so the final tests would seem more similar to easy to read and maintain.

    before(:each) do
    @login_page = Login.new
    end

    it "should successfully login" do
    @login_page.click_on 'Sign In'
    @login_page.login 'twtest', 'test123'
    @login_page.succesfully.should == true
    end

    Then the tests doesn't even require the watir components on tests script.

    ReplyDelete
  3. Then the tests doesn't even require the watir components on tests script?????????????????
    isabel marant cleane boots(dsf2012.4.11)

    ReplyDelete
  4. Merci
    aswqergh
    Probably one of the most talked about stores in Paris, Isabel Marant Flat

    ReplyDelete
  5. Thanks for this post. I have some thoughts about DRY page objects over at my blog: http://burdettelamar.wordpress.com/2014/03/21/keep-your-page-objects-dry/

    ReplyDelete