Screenshots of Failing Cucumber Scenarios

14. September 2009

At 7digital we use Cucumber and Watir for running acceptance tests on some of our websites.

These tests can help greatly in spotting problems with configuration, databases, load balancing, etc that unit testing misses.

But because the tests exercise the whole system, from the browser all the way through the the database, they can tend be flakier than unit tests. Then can fail one minute and work the next, which can make debugging them a nightmare.

So, to make the task of spotting the cause of failing acceptance tests easier, how about we set up Cucumber to take a screenshot of the desktop (and therefore browser) any time a scenario fails.

Install Screenshot Software

The first thing we need to do is install something that can take screenshots. The simplest solution I found is a tiny little windows app called SnapIt. It takes a single screenshot of the primary screen and saves it to a location of your choice. No more, no less.

Tell Cucumber to Take a Screenshot When a Scenario Fails

Now we need to tell Cucumber to take a screenshot. To do so we’ll add a function to the Cucumber World that will take a screenshot if needed, and run this in the After scenario hook. To do this modify your features/support/env.rb file.

env.rb

class DefaultWorld

  # Screenshot directory, relative to this env.rb file
  DEFAULT_SCREENSHOT_PATH = File.expand_path(File.dirname(__FILE__) + '/../../../output/cucumber/screenshots/')

  # Absolute location of SnapIt
  SNAPIT_PATH = 'C:\\Tools\\SnapIt.exe'

  def take_screenshot_if_failed(scenario)
    if (scenario.status != :passed)
      scenario_name = scenario.to_sexp[3].gsub /[^\w\-]/, ' '
      time = Time.now.strftime("%Y-%m-%d %H%M")
      screenshot_path = DefaultWorld::DEFAULT_SCREENSHOT_PATH + '/' +  time + ' - ' + scenario_name + '.png'
      cmd = DefaultWorld::SNAPIT_PATH + ' "' + screenshot_path + '"'
      %x{#{cmd}}
    end
  end

  # [...] Other DefaultWorld code here if needed

end

World do
  DefaultWorld.new
end

After do |scenario|
  take_screenshot_if_failed(scenario)

  # [...] Other After hook code here if needed

end

Just modify the constants in the above code to point to the locations of SnapIt and a directory to save the screenshots too.

What the Code Does

The code will only take a screenshot if the scenario fails to pass.

It then extracts the name of the scenario, and converts it to a filename friendly string (e.g. Monkey's should eat "things" => Monkey s should eat things). It then prepends the current date and time, and uses this string as the filename for the screenshot.

This allows you to easily find screenshots for a specific scenario or time.

Run a Failing Test and Check Out the Screenshot

Now you can run Cucumber as normal, watch a test fail, and you should see a screenshot appear in the directory you specified. And hopefully it will help you work out what went wrong, enjoy!

If the screenshot fails to appear, it could be because of an error in the ruby code. But Cucumber seems to hide any execptions within the After hook, so you may need to add puts statements to work out what is going wrong.