Switching from Capybara Rack-Test to Poltergeist

We’re upgrading our current project from a traditional multi-paged app to a single-page app. Of course, this means that our integration testing layer will need to change, but so far that’s been a reasonably calm sea.

Choosing a Driver

Following Larry Price’s quick tutorial got me running Poltergeist instead of Capybara-Webkit initially because that’s the order he described them in. I’m so far happy with the choice, and there are rumbles on the Internet that CW could use improvement, but it’s likely just strong in its own ways. In any event, Price’s multi-window woes don’t apply to this project yet, so we’ll forge ahead with Poltergeist and PhantomJS.

I can, however, definitely confirm that running Selenium is slower than the buraucracy at a molasses factory run by Ents 1. Friends don’t let friends TDD with Selenium.

There is a small caveat to running Poltergeist vs running Rack-Test: Poltergeist seems to fail tests much slower. I’m not too worried about it, though, since it would only be punishing if you constantly ran with failing tests.

Installing Poltergeist

To get Poltergeist running, you first need the headless Javascript engine it runs, PhantomJS. Go download and unpack the appropriate tarball into your /usr/local/share/ directory, and then link 2  the binaries into somewhere your path will get it. 3

I chose to link it thusly:

  sudo ln /usr/local/share/phantomjs-1.9.7-linux-x86_64/bin/phantomjs /usr/local/share/phantomjs
  sudo ln /usr/local/share/phantomjs-1.9.7-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
  sudo ln /usr/local/share/phantomjs-1.9.7-linux-x86_64/bin/phantomjs /usr/bin/phantomjs

… though you could probably just do the second one.

Turning It On

Next, install the 'poltergeist' gem. After that’s done, you need to tell Capybara to use it by adding this to your cucumber env.rb file.

# in env.rb:
# ...
Capybara.default_driver = :poltergeist

This will now use poltergeist for all tests.

Some Tailoring and Hiccups

HTTP Methods

We have some tests that directly talk to the web API, but Poltergeist’s driver doesn’t allow you to run those HTTP methods in the same way. The easy solution I found was to create the inverse of Capybara’s @javascript tag, which I tagged @no-js. This switches the driver back to the normal, Javascriptless Rack-Test, and lets us use our .follow trick from earlier.

Before '@no-js' do
  @normal_driver = Capybara.default_driver
  Capybara.default_driver = :rack_test
After '@no-js' do
  Capybara.default_driver = @normal_driver

Element Visibility

This was the most sneaky difference so far. Apparently, different drivers are allowed to have different default interpretations of what is “visible” to them. Rack-Test finds hidden form elements 4, while poltergeist does not by default. You can force visibility on, though, by providing visible: true to your Capybara find statements.

Now, next on my list is to be able to type poltergiest Poltergeist correctly on the first try.


  1. Over a minute with Selenium vs at most 18 seconds with Poltergeist
  2. Kudos to StackOverflow users shawn and Ciro Santilli: http://stackoverflow.com/questions/8778513/how-can-i-setup-run-phantomjs-on-ubuntu
  3. Either that, or add the binary directly to your path. You’re on your own for that one.
  4. Before anyone cries foul, we need to set timestamp form variables to make a deterministic test.

Robin Miller


View more posts from this author
One thought on “Switching from Capybara Rack-Test to Poltergeist
  1. Robin Miller

    We hit our first small snag: save_and_open_page doesn’t load CSS or JS (by design, it seems). However, following Lime’s comment on this blog post (https://coderwall.com/p/jsutlq) seems to work for us. You’ll need to play with the port number, but it’s a start. Here’s his comment:

    Capybara also supports setting an asset_host as follows:

    Capybara.asset_host = ‘http://localhost:3000′
    This will insert a tag with the asset host into the pages created by save_and_open_page, meaning that relative links will be loaded from the development server if it is running. The end result is pretty much the same[...]“