Lapis Lazuli:Why use lapis lazuli over watir as a ruby test automation solution

From Test Automation Wiki
Jump to: navigation, search

Lapis Lazuli is a layer on top of Watir Webdriver, so it has all the functions Watir has, and more!

Often the question is asked why someone would use it, if Watir can do anything they need to do automating a website. The short answer is: LL enforces the developer to use best practices over quick and dirty solutions without costing extra effort.

So what are the advantages:

Error reporting

» More detailed, can suppress error, easy customized error message.

Watir

button = browser.button(:text => 'NotExistingElement')
#=> #<Watir::Button:0x..f908e3510 located=false selector={:text=>"NotExistingElement", :tag_name=>"button"}>
button.click
#=> Watir::Exception::UnknownObjectException: unable to locate element, using {:text=>"sadfsad", :tag_name=>"button"}

In Watir an error is thrown after you try to interact with an element instead of the moment you try to search for the element. Also, to do a customized error, you have to catch the default error with your custom text.

Lapis Lazuli

button = browser.find(:button => {:text => 'NotExistingElement'})
#=> RuntimeError: Error in find - Cannot find elements with selectors: {:pick=>:first, :mode=>:match_one, :selectors=>[{:button=>{:text=>"NotExistingElement"}}]} [ https://www.google.nl/?gfe_rd=cr&ei=QGqgV4jRL7DH8AfVmq2IBg&gws_rd=ssl ]

button = browser.find(
  :button => {:text => 'NotExistingElement'},
  :message => "The button was not found, but that's no problem, we didn't want to find it anyway."
)
#=> RuntimeError: The button was not found, but that's no problem, we didn't want to find it anyway.
button = browser.find(
  :button => {:text => 'NotExistingElement'},
  :throw => false
)
unless button
  #Do something else
end

In Lapis Lazuli the default error already contains 'readable text', an automatic screenshot is taken and the URL is shown where the problem occurred. Besides that, it's also easy to customize the error message to your own likes. If a not-found element is not necessarily an issue, you can simply suppress the error with :throw => false

Finding elements

Prevents using regular expressions (quicker)

Watir

browser.goto 'tweakers.net'
browser.as(:class => /e/).length
# Returns 111 after 3 seconds

Lapis Lazuli

browser.goto 'tweakers.net'
browser.find_all(:like => [:a, :class, 'e']).length
# Returns 111 after 0.5 seconds

Lapis Lazuli provides the :like functionality, which is handy for multiple purposes. In the above it enables us to look for elements that include the letter "e" rather than using a regular expression. Doing this using a lot less processing (600%!). On a small scale it might not matter much, but on a larger projects, these seconds can mean 2 hours of running versus 15 minutes.

Easier to match on multiple attributes

HTML

<div class="fox" id="red"><img src="1"/></div>
<div class="fox" id="blue"><img src="2"/></div>
<div class="fence" id="red"><img src="3"/></div>
<!-- I know, an ID that's no unique, like that ever happens!? :-) -->

» Select the div with image src="1"

Watir

browser.divs(:class => 'fox').each{|elm|
  if elm.id == 'red'
    selection = elm
  end
}
print selection.html
# "<img src="1"/>"

Lapis lazuli

selection = browser.find(
  {:div => {:class => 'fox'}},
  {:div => {:id => 'red'}}
)
print selection.html
# "<img src="1"/>"

No need for wait loops

In automation, often you have to wait for an element to become visible. Either because of something loading with Javascript or a page between your last and next action.

Example: We've clicked the login button and are waiting for <div id="login_form">(...) to become visible.

Watir

starttime = Time.now
wait_time = 60
login_present = false
while !login_present and Time.now-starttime<wait_time
  if browser.div(:id => 'login_form').present?
    login_present = true
  end
  sleep 0.5
end
unless login_present
  raise "Login field did not become present after #{wait_time} seconds!"
end

Lapis Lazuli

browser.wait(:div => {:class => 'login_field'}

The above is enough. It will wait a default of 10 seconds and will show a default error if the field is not found withing the given time. A more extended version would be the following:

browser.wait(
  :div => {:class => 'login_field',
  :timeout => 60,
  :message => "Login field did not become present after 60 seconds!"
)

LL also supports :condition => :while, in which case it would wait as long as the element is present.

Interaction with an array of results

<div class="product">1</div>
<div class="product">2</div>
<div class="product">3</div>
<div class="product">4</div>

» Select a random product

Watir

prods = browser.divs(:class => 'product')
amount = prods.length
selection = prods[rand(amount-1)]
print selection.text

Lapis Lazuli

prods = browser.find_all(:div => {:class => 'product'})
selection = browser.pick_one(:random, prods) 
print selection.text

Can look for custom attributes

<div class="product" http-active-value="false">1</div>
<div class="product" http-active-value="false">2</div>
<div class="product" http-active-value="true">3</div>
<div class="product" http-active-value="false">4</div>

» Select the element with http-active-value set to true

Watir

active_elm = browser.div(:xpath , "//div[@http-active-value='true']/")
print active_elm.text

According to [1] page.

Watir

active_elm = browser.find(:like => [:div, :http-active-value, 'true']
print active_elm.text

From our point of view, your code will be better readable if you're not using Xpath.

Debugging

Debugging is almost the same. Start by running irb in your command line.

Watir

require 'watir-webdriver'
browser = Watir::Browser.new
browser.goto 'google.com'

Lapis Lazuli

require 'lapis_lazuli'
include LapisLazuli
browser.goto 'google.com'

After including LapisLazuli all functions of Watir will be available, next to the LL functions.

Just as simple to start in IRB

Watir

require 'watir-webdriver'
browser = Watir::Browser.new
browser.goto 'spritecloud.com'
browser.a(:text => 'about us').click

Lapis Lazuli

require 'lapis_lazuli'
include LapisLazuli
browser.goto 'google.com'
browser.find('about us').click

Simple remote testing (beta)

Lapis Lazuli supports Selenium grids out of the box:

browser :remote, {
  :url => "http://hub.browserstack.com/wd/hub/"
  :user => "USERNAME",
  :password => "PASSWORD_OR_APIKEY",
  :caps => {
    "browser" => "Firefox",
    "browser_version" => "34.0",
    "os" => "OS X",
    "os_version" => "Yosemite",
    "resolution" => "1024x768"
  }
}

Build in screenshot system

Project level

Build in Configuration variables