Talking testing, automation... and anything else.

Feb 13

I Heart My Chromebook – A Review

chromebookI’ve always been a fan of smaller laptops (my 13″ Macbook is a personal fav) so when Google released their Chromebooks, they had my attention. I eventually couldn’t resist and picked up the Samsung, arm-based Chromebook XE303 for the extremely reasonable price of $249.

After having a couple weeks on it, I must say I really REALLY like it. It’s been shockingly easy to abandon my Macbook in favor of my smaller, lighter Chromebook. In fact, there’s very few things in my daily (non-work) routine that I can’t do on the Chromebook… Chrome Remote Desktop solves the rest.

What’s good:

  • Price!
  • The keyboard and trackpad are great and responsive
  • Battery life – @4.5 hours video; @6.5 hours surfing/etc…
  • Small and light
  • Boots in seconds; wakes instantaneously
  • ChromeOS is simple, intuitive and impressively powerful
  • Chrome Remote Desktop bridges most gaps
  • Overall design is very “Mac-like”
  • Many great, free apps/extensions
  • Setup. If you use Google/Chrome, once you sign in, all your stuff is already there

The bad:

  • The screen is kinda crappy… though certainly servicable
  • Lack of AirTunes equivalent… Google, please make this happen!
  • Lack of full *nix shell is bothersome
  • Power adapter is clunky; wish it was micro usb

One thing that might put ChromeOS over the top for me (and I’m sure I’m not alone) is to allow access to its underlying Linux distro. Like OS X, ChromeOS is just a window manager on top of unix (in this case Linux). I would love to see them continue the trend and allow access to it’s Linux underbelly. In short, give me a full shell!

Long story only slightly longer, this Chromebook is a keeper and has me thinking devious thoughts about selling the Macbook and in its stead, getting a Mac Mini server to command via CRD! And I guess I’ll need a new signature… something like:

Written on my Chromebook… or maybe not (even if it is true).

Jan 13

Watir-Webdriver Sort Test

watir.logoWhile convalescing this past weekend from some kinda cold/funk that’s been making the rounds, I spent some time playing with Watir-Webdriver. I really like Watir and have heard good things about Webdriver, so I wanted to see for myself. As an exercise, I decided to port my Sahi sort test from a previous post… here’s what I came up with.

# ~ sort test...

require 'rubygems'
require 'watir-webdriver'

browser = Watir::Browser.start '', :chrome

# initial sort... => 'Name').click

# collect table column elements in an array... 
app_sorted_values =
# itterate through each row (skip the 1st)... 
browser.table(:id, 'table0').rows.each.drop(1).each do |row|
	app_sorted_values << row.cell.text

# sort with ruby case-insensitive... 
ruby_sorted_values = app_sorted_values.sort {|a,b| a.upcase <=> b.upcase}

puts "App sort: #{app_sorted_values} \nRuby sort: #{ruby_sorted_values}"


Do feel free to comment and/or make suggestions!

Jan 13

Selenium-WebDriver vs Sahi

selenium.logoJari Bakken has a couple of nice example tests that show some of the differences between a Selenium-WebDriver test and a Watir-Webdriver test. I thought I would create another example to show how the same code could be written in Sahi.

Both of the following scripts perform the same test. The test itself is simple:

  1. Go to the Google Translate page
  2. Click the Detect Language button
  3. Select Norwegian as your language
  4. Log the button’s text
  5. Enter the word “ost” into the text field
  6. Verify “cheese” is the returned translateion

Selenium-Webdriver Example…

require 'selenium-webdriver'

driver = Selenium::WebDriver.for :firefox
driver.get ""

wait = => 5)

language_button = wait.until {
  element = driver.find_element(:id => "gt-sl-gms")
  element if element.displayed?

language_button.find_element(:tag_name => "div").click

menu = wait.until {
  element = driver.find_element(:id => "gt-sl-gms-menu")
  element if element.displayed?

langs = menu.find_elements(:class => "goog-menuitem")

norwegian = langs.find { |lang| lang.text == "Norwegian" }
norwegian.find_element(:tag_name => "div").click

puts language_button.text

driver.find_element(:id => "source").send_keys("ost")

result = wait.until {
  result = driver.find_element(:id => "result_box").text
  result if result.length > 0

puts result

Sahi Example…



_log("The selected button text is: " + _getText(_div("gt-sl-gms")));

_setValue(_textarea("source"), "ost");

_assertEqual("cheese", _getText(_span("result_box")));

The difference is pretty dramatic… at least in test size. Sahi handles all your waits for you so there’s no need to clutter up your tests with wait code. Accessor code is also less verbose in Sahi. Overall, I would argue the Sahi code is much more readable but code beautry, like beauty in general, is in the eye of the beholder. I.E. your millage may vary…

Note: I used an assert instead of just logging the returned translateion… same dif

UPDATE: I’ve also added Geb to the fray!

Jan 13

Verify Sorting With Sahi (or any tool really)

4800819674_3cf963deaa_bI recently found a bug when sorting table columns, while running IE9. Ignoring the obvious fix, I wrote up the bug and then as I’m a fan of doing, I wrote a failing automated test to test it (Defect Driven Development!).

Testing sort proved a bit tricky… I thought I would share the results to perhaps save others same pain. I would also not be surprised to find a more elegant solution out there. If you have one, do feel free to share!

My example is in Sahi but it should be easy to transfer to your tool of choice. The gist is:

  1. Sort your column
  2. Collect all the elements in the column in an array
  3. Copy the array and sort the copy using javascript’s sort()
  4. Compare the two arrays

And here’s the Sahi code…

/** ~sort test... 	**/


// initial sort... 

// collect table column cell values in an array...
_set($numRows, _table("table0").rows.length -1);
var $appSortedValues = new Array();
for(var $i=0; $i<$numRows; $i++) {
	$appSortedValues[$i] = _getText(_cell(0, _in(_row($i+1, _in(_table("table0"))))));

// copy array javascript style using slice... 
var $jsSortedValues = $appSortedValues.slice(0);

_assertEqual($jsSortedValues, $appSortedValues);

// Javascript's sort is case sensitive so we "fix" that thusly... 
function caseInsensitiveSort(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;

First off, thanks to for providing an example for my example!

The script starts by navigating to and clicking on the Name table header to get our initial sort of that column.

Then we collect each element in the Name column, in table0 and store them in an array, $appSortedValues. To iterate through our loop, we get the number of rows in table0 by counting the number of rows and subtract 1 for each table header.

Now we need a copy of our $appSortedValues array but in Javascript, you can’t just set a new array from our existing array like so: $jsSortedValues = $appSortedValues; . This will actually create a reference to our original array; not what we want. Instead we use the slice() method to select elements 0 through the end of the array and put them all in our new array, thus copying it.

Finally, we use javascript’s sort() method to sort the copy of our array, $jsSortedValues. But the sort() method has a little wrinkle; by default, it sorts alphabetically and is case sensitive. This is unlikely to be how your application’s sort works… but luckily, you can roll your own sort filter by passing a function as an argument to the sort() method. In our case, we want it to be case-insensitive, hence our function caseInsensitiveSort.

Now we have two arrays and can simply use Sahi’s _assertEqual method to verify both arrays are sorted in the same order.

That’s it!

Apr 12

Accessing Browser Objects Using Sahi

Sahi is a great automation tool for testing web applications. While Similar to Selenium, Sahi offers many notable advantages over Selenium, including some that were deal breakers in my current situation (IE support for instance).

Sahi also offers some ways to access page objects that are just flat out awesome. I’ll discuss a few here, specifically strings, indexes and it’s built-in Browser Accessor API _under.

Consider the following HTML table…

Last Name First Name
Deng Luol
Rose Derrick
Gibson Taj

Suppose you had a story that called for you to click a link from the table above. Simple enough… in Sahi you could write something like this:

_click(_link("Deng")); // opens Luol's Wikipedia page

Yup. This code does indeed open Lu’s Wikipedia page as expected. It’s using the link text to access the page’s object (not an xpath). Handy for sure but what if you had other sites to test that contained different data in this same table (hopefully not any Miami Heat Players)? Or maybe these links are commonly deleted by other scripts you’re running? The code above would fail.

No problem. Sahi indexes all page objects which allows you to access the “nth something” on the page. In this example, we could just click the first link. And in case there are other links elsewhere above, we’ll be more specific and make use of one of Sahi’s fantastic relational accessors, _under. So…

_click(_link(0, _under(_tableHeader("Last Name"))));

Lu’s Wikipedia page again appears! What’s happening here is we’re clicking the first link (indexes are zero-based a thus start with zero) under the table header “Last Name”. Thanks to the indexing of the links, we could also iterate through all of the links via a simple for loop.

But now maybe we’d like to take this up a notch and inject some randomness that will help broaden our testing scope and perhaps improve our chances of finding a bug (the purpose of testing after all). And maybe we’ll also make it a function to help keep our automation more readable. So perhaps something like…

function clickRandomPlayerLink() {
	// _count's index is NOT zero-based; starts at 1
	var $numPlayers = _count("_cell", "lastname")-1; 
	// get random number between 0-$numPlayers (2)
	var $playerIndex = _random($numPlayers); 
	_click(_link($playerIndex, _under(_tableHeader("Last Name"))));

This function uses Sahi’s _count method to count the number of cells that have an id of lastname. (Note: because the results of _count are NOT zero-based (starts with 1) it returns “3” so we just subtract 1. We then use Sahi’s method _random to get a random number between 0 and $numPlayers (2). Now we call the same code as in our previous example to click one of the player’s links at random, under the table header “Last Name”.

And now any one of my Bulls player’s Wikipedia pages is displayed when running the script! These are just a few of the ways Sahi lets you get at page objects… there’s a bunch more including _leftOf and _rightOf, new to Sahi Pro 4.2.