Lapis Lazuli:Why use lapis lazuli over watir as a ruby test automation solution
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 it 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.