Watir reports with screenshots using RSpec and Win32::Screenshot

Posted by aslak.hellesoy

At one of the projects in BEKK we’re using Watir extensively to verify that the web application is working as expected. We’re using RSpec to drive Watir.

Every now and then the Watir specs fail (either on a developer machine or on the Continuous Integration machine). For each failure we want to know what HTML the browser had, and what the browser looked like at the time of failure. Knowing this makes the process of hunting down the cause much easier, because it gives us more context information.

Watir comes with built-in support for screenshots via Watir::ScreenCapture, but it is a really nasty hack so we didn’t want to use that. We also wanted a nice-looking HTML report for all the specs, with a screenshot of the browser (and a link to the HTML) for each of the failing specs.

About the same time as I released Win32::Screenshot, Luke Redpath contributed a much improved HTML report for RSpec. (The RSpec website has some more examples of passing and failing specs).

Getting screenshots into the RSpec HTML reports is quite easy. First, in the spec’s teardown we have to take a screenshot and store it in a file with a unique name. (I’ve simplified the code a little here to illustrate the technique – the full code is available in RSpec’s svn). Let’s start with the spec:


context "Google's search page" do
  setup do
    @browser.goto('http://www.google.com')
  end

  specify "should find rspec's home page when I search for rspec" do
    @browser.text_field(:name, "q").set("rspec")
    @browser.button(:name, "btnG").click
    @browser.contains_text("rspec.rubyforge.org").should_not_be nil # should_contain_text is RSpec sugar
  end

  specify "should find rspec's home page when I search for 'better than fudge' (will probably fail)" do
    @browser.text_field(:name, "q").set("better than fudge")
    @browser.button(:name, "btnG").click
    @browser.contains_text("rspec.rubyforge.org").should_not_be nil
  end

  specify "should not find Ali G when I search for respec" do
    @browser.text_field(:name, "q").set("respec")
    @browser.button(:name, "btnG").click
    @browser.contains_text("Ali G").should_be nil
  end

  teardown do
    # Take the screenshot 
    width, height, bmp = Win32::Screenshot.foreground
    img = Magick::Image.from_blob(bmp)[0]
    # dir and spec number are defined outside
    img_path = "#{dir}/#{spec_number}.png" 
    img.write(img_path)

    # Get the HTML from Watir and save it too
    File.open("#{dir}/#{spec_number}.html", "w") {|io| io.write(browser.html)}
  end
end

RSpec will output a HTML report when you run it with —format html, but it doesn’t link to images or the HTML source. We have to write a little extension to RSpec’s default HTML formatter:


class WebTestHtmlFormatter < Spec::Runner::Formatter::HtmlFormatter
  def extra_failure_content
    @output.puts "
" @output.puts "" end end

Finally we have to tell RSpec (which is driving Watir via our specs) to use our WebTestHtmlFormatter when it runs the specs:


spec --require web_test_html_formatter.rb --format WebTestHtmlFormatter

The astute reader might have noticed that in the clickable screenshot above I have a Safari window and not an Internet Explorer one. The fact is, you can use this very same technique on a Mac, using Dave Hoover’s SafariWatir. The only difference is that you’ll use OS X’ built-in screencapture command to take the screenshots.

If you want to try this out, install the latest RSpec (0.7.4 or later). Then check out RSpec with subversion and peek inside vendor/web_spec. (The path within the repository may change soon).

Comments

Leave a response

  1. meekishDecember 02, 2006 @ 10:38 PM

    This is very nice. And Luke did a beautiful job on the HTML output.

    It’s also great to see that you have a blog now. I just can’t get enough of rSpec!

  2. Trond Arve WasskogDecember 04, 2006 @ 04:27 AM

    Any idea how to implement this with Selenium?

  3. Aslak HellesøyDecember 04, 2006 @ 04:38 AM

    You can do this with the SeleniumRC Ruby bindings. Essentially it gives Selenium a Watir-like API that allows you to drive the browser from Ruby.

    If you have already invested in lots of HTML-style Selenium tests, you may be able to hack Selenium-RC so that it can push those HTML tests up to the browser (if it doesn’t support that already – I’m not sure).

    Another option could be to hack Selenium to send a HTTP request to a screenshot server. This is a tiny HTTP server that takes screenshots when you hit it with /foreground or /desktop – there is an example in Win32::Screenshot’s subversion.

    And if that doesn’t work for you, use your imagination ;-)

  4. Johannes BrodwallDecember 06, 2006 @ 05:35 AM

    Sweet. The HTML output with the screenshot is especially nice. But about RSpec: It would be nice to improve on the diagnostic message “nil should not be nil”. Is that at all possible?

  5. Aslak HellesøyDecember 06, 2006 @ 06:10 AM

    Johannes: Anything is possible!

    The same topic has come up before – see the mail thread that starts here

    You’re welcome to submit an RFE, patch or whatever for this.

  6. Jonathan VineyDecember 06, 2006 @ 06:41 AM

    meekish, I’ve done some work to integrate Rails and SeleniumRC.

    http://svn.viney.net.nz/things/rails/plugins/selenium_jelly/

  7. Stefan LandrøJanuary 10, 2007 @ 07:24 AM

    Hey Boss, how about grabbing screenshots that can be used by the customer as documentation (integrating all shots in a nice PDF would be the next step)

  8. Stefan LandrøJanuary 10, 2007 @ 07:25 AM

    Hey Boss, how about grabbing screenshots that can be used by the customer as documentation (integrating all shots in a nice PDF would be the next step)

  9. hjo1602February 14, 2007 @ 09:00 AM

    This produces a file of 1996kB,
    that MSPaint displays as blank. It’s got a BMP header, but the rest of the file is Hex 00’s

    def screenShot
    fname = 'hej.bmp'
    width, height, bmp = Win32::Screenshot.foreground
    file = File.new(fname, "wb")
    File.open(fname, "wb") {|io| io.write(bmp)} unless file.nil?
    end

    Any ideas why ?

  10. RaviMarch 01, 2007 @ 06:27 AM

    How can i start to write a program. Pls exolain me.

    I have these specilities. 1. Watir 2.wet

    Please give one example program to take screenshot of any web page

  11. RailRoadRubyApril 20, 2007 @ 11:12 PM

    How to use this? I could not get to your example. Can you post some Full Examples?. I just keep getting the error undefined method `context’ for main:Object (NoMethodError). My whole day is gone

    Shaun