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:
Selenium
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.
PhantomJS
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"
end
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
Capybara.reset!
end
teardown do
Capybara.reset!
end
end
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'))
end
end
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.