February 21, 2015 by Daniel P. Clark

Minitest with Watir-webdriver

Why, you might ask, would I even think of Watir-webdriver?  Well let me tell you.  It’s because it’s easy and fun.  It’s not a fast way to run tests though.  What Watir will do for you is open an actual web browser window and let you check and interact with it from your Ruby code.

The thing I love about Watir is that it’s pretty intuitive by design.  You initialize your browser with Watir::Browser.new and then you call the goto command with the string of the url you want to go to.  You can do it in one go if you use Ruby’s tap method.  (Be sure to require ‘watir-webdriver’)

b = Watir::Browser.new.tap {|i| i.goto “localhost:3000” }

With this command you have a browser window already opened and the website pulled up.  You can use the variable you’ve set to access whatever you’d like from within your site.  Here are some examples:

# first link
b.link
# all the links in the page
b.links
# the first button
b.button
# all of the buttons on the page
b.buttons

b.text.include? "Welcome!"
# => true/false result

When you type b.links or b.buttons it doesn’t look like anything is there at first. When exploring your options with Watir it’s good to call the .methods method on each item to see what’s available.  In essence these are enumerables.  You can call .to_a to get the full list.

It is very likely that some page Objects are hidden in the browser but still show up in your list.  When performing actions on the page like b.link.click the link must be visible.  You can choose just visible objects by using select: a.links.select(&:visible?) .  From there you can check each item with the intuitive methods like .href, .id, .name, .target, etc.  So as you can see working with Watir to automate your browser is a lot like just working with regular HTML and Ruby.

Minitest

When getting into testing websites in Rails there are so many complicated things which lead to a large learning curve.  How do you test Devise?  What about JavaScript objects?  If you haven’t already learned these things then they each may bring some challenges and will be slow to start.  But if you’re not worried about the speed of your tests, then why not use something utterly simple?

Here’s what a Devise login looks like in Watir:

b = Watir::Browser.new
b.goto 'localhost:3000'
b.links.select {|i| i.href =~ /sign_in/}.first.click
b.wait_until { b.text.include? "Remember Me"  }
b.text_field(id: 'user_email').set("[email protected]")
b.text_field(id: 'user_password').set("password")
b.form.submit
b.wait_until { b.text.include? "Signed in successfully." }

That’s it!  And the cool thing is is that their isn’t anything saying that this has to be Devise.  This would work with any login page on any web application with just a few reference changes.

The wait_until methods can be implemented in many different ways.  You can tell it to wait 1 second and then use the page result for your test ( b.wait_until {sleep 1; true} ).  In my experience using b.text.include? will wait on its own for the page to completely load.  If you’re waiting on some additional work from JavaScript (or the like) then you should give it a target to wait for.

When using Watir during tests it opens each browser window in its own sandbox like environment.  That means no previous session cookies are involved.  So for a website with a Devise login you need to put your login logic into your setup block and close the browser in your after block.

Here’s a working sample I put together today for my Rails project.

# test/watir_test.rb
$:.<<('./').<<('test/')
require 'test_helper'
require 'minitest/autorun'
require 'watir-webdriver'

class WatirTest < ActiveSupport::TestCase

  setup do
    @b = Watir::Browser.new
    @b.goto 'localhost:3000'
    unless @b.links.any? {|i| i.visible? && i.href =~ /sign_in/}
      @b.buttons.first.click
    end
    @b.links.select {|i| i.href =~ /sign_in/}.first.click
    @b.wait_until { @b.text.include? "Remember Me"  }
    @b.text_field(id: 'user_email').set("[email protected]")
    @b.text_field(id: 'user_password').set("password")
    @b.form.submit
    @b.wait_until { @b.text.include? "Signed in successfully." }
  end

  it "takes feedback" do
    unless @b.links.any? {|i| i.visible? && i.text =~ /Send Feedback/}
      @b.buttons.first.click
    end
    @b.links.select {|i| i.visible? && i.text =~ /Send Feedback/}.first.click
    @b.wait_until { sleep(2); true }
    @b.textareas.select(&:visible?).first.set("Rumplestiltskin")
    @b.buttons.select {|i| i.value =~ /Submit Feedback/ }.first.click
    @b.text.include?("Rumplestiltskin").must_equal true
    @b.links.select {|i| i.text =~ /Delete/}.first.click
    @b.alert.ok
    @b.text.include?("Feedback was successfully deleted").must_equal true
  end

  it "logs out of website" do
    unless @b.links.any? {|i| i.visible? && i.href =~ /sign_out/}
      @b.buttons.first.click
    end
    @b.links.select {|i| i.href =~ /sign_out/}.first.click
    @b.text.include?("Signed out successfully.").must_equal true
  end

  after do
    @b.close
  end
end

Now generally you won’t need to build something as complicated as this.  What I’m dealing with in this example includes Bootstrap mobile views which hides the links.  That’s why I check if the links are visible, and if they’re not I click the first button (which is the dropdown menu for mobile).  Then the links are available and I continue.

The Submit Feedback part of my minitest spec was involving a JavaScript fade in, and I had two fields with the same ID within the page.  But when one became visible in the modal, then the one in the footer became hidden.  This made it very difficult to use a standard way to check if it was visible and to write to it because there are conflicting object references.  So the easy solution was to be generic with it.  Instead of waiting for a specific identifier I waited 2 seconds.  Then I just selected the first textarea that was visible and wrote to that.

Results

I had originally prepared for multiple browser windows to open and share session data.  But that’s not what happened.  When I ran the tests they did each open their own browser window, but they waited for one test to complete before the other would start in a new window.  If there’s an error in the test code it errors out immediately for that particular test.  If the site waits for something and it doesn’t show up it’ll time out after 30 seconds (you may be able to change the timeout).  Each test performed a complete login and closed the browser window when finished.  The log out test with the login took 11 seconds consistently.  The feedback test with login, feedback entry, and deletion was about 15 to 18 seconds.  So yes the tests are considerably slow.  But it’s also fun to watch.

Summary

Well the tests didn’t perform asynchronously for me, but overall I’m happy with the simplicity of it.  Depending on your situation this may be a good option for you.  Just keep in mind this is not a quick option.  Watir is beautiful, simple, intuitive, and fun.  Give it a try if for no other reason than to watch it take over your browser.

Watir was the first tool I learned when I started in Ruby.  I was hired as a Ruby developer not even knowing Ruby existed as a language; purely on a recommendation from a friend.  So I dove in and did well with it.  I like Watir.  I’ve used Mechanize, Nokogiri, and Selenium (Watir includes this).  Of everything I’d choose Mechanize first for web automation (which include Nokogiri); that’s just because I prefer the headless web experience (here headless means you don’t see a browser that’s emulating a human interaction, it’s background work).  But I still give Watir my high praise for their wonderful design.  It’s well documented as well: rubydoc.info/gems/watir-webdriver/  but I’d still advise using irb to discover the methods.

Hopefully you found something new and fun with this!  Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

Image by Michele Ursino via the Creative Commons Attribution-ShareAlike 2.0 Generic License.

#browser#chrome#firefox#ie#minitest#rails#ruby#safari#spec#test#watir#watir-webdriver#web

Leave a Reply

Your email address will not be published / Required fields are marked *