@madpilot rants

Testing UI with Integration tests in Rails with phantomjs

Computers aren’t very good at testing visual changes on websites. Sure you can test the differences between two images, but image changes are really brittle. However, humans are pretty good at it. As a result, the common way to test visuals it to actually click through stuff on a browser. This doesn’t scale. For one, if your UI changes based on different initial states, it can take ages to set this stuff up.

Thankfully there is a fairly simple solution.

Rails integration tests can control with a number of browsers using Capybara and a automation system like Selenium or PhantomJS. If you haven’t heard of these, Selenium automates real browsers, and PhantomJS¬†is a headless webkit browser. Both support modern CSS and JavaScript, so it’s like having a 1000 monkeys on a 1000 UNIX terminals. It just so happens that both also support taking screenshots, which we can use as a poor-man’s Mechanical Turk.

Aside: There is another solution: Using something like BrowserStack, although because it works remotely, it might make setting up the testing environment between tests difficult. I’ll look at this later in another blog post once I get a chance to play with it.

There are pros and cons to both systems:


Pro: Can control basically any browser that it has a driver for – including Android and iPhones.
Con: It requires Java and a computer with all of those browsers and emulators installed. It’s also the slower of the two.


Pro: Is headless, so doesn’t require X windows, or any other window manager.
Con: It’s WebKit only.

I’ll go through setting up PhantomJS¬†on OSX, because that’s what I’ve set up so far; I’ll leave setting up Selenium as an exercise for the reader – the theory should be the same.

First, you need to install PhantomJS. On OSX, it’s just a matter of using homebrew

brew install phantomjs

Next, add the poltergeist gem (which is an interface to PhantomJS. It will also install Capybara)

group :test do
    gem "poltergeist", "~> 1.3.0"

Add the following to your test/test_helper.js

require 'capybara/rails'
require 'capybara/poltergeist'

Capybara.default_driver = :poltergeist
Capybara.javascript_driver = :poltergeist

class ActionDispatch::IntegrationTest
  include Capybara::DSL

  setup do

  teardown do

Finally, generate a integration test

rails generate integration_test name_of_the_test

Open up the Integration test and create a test

require 'test_helper'

class NameOfTheTestIntegrationTest < ActionDispatch::IntegrationTest
    # Don't forget this line - Database transactions don't work well with Capybara
    self.use_transactional_fixtures = false

    test 'some test name' do
      visit '/'
      # This is the important line. This is what saves the screenshot
      save_screenshot(Rails.root.join('tmp', 'screenshots', 'name_of_the_integration', 'some-test-name.png'))

Run the integration test, and you will find a nice 1024×768 PNG screen shot in the tmp/screenshots/name_of_the_integration/some-test-name.png directory.

So, after your tests run, you can scan through the screen shots and see if anything looks weird. Win!

For a more in-depth instructions for running tests in Capybara, checkout their GitHub page.