Rails4 Javascript-integrated unit tests via PhantomJS, RSpec, and Capybara

In this blog post, I'll share some code that sets up a basic configuration to do Javascript testing via RSpec and Phantomjs.

If you don't already have PhantomJS, you can install it via Homebrew.

brew install phantomjs

For this post, I ran a few commands to setup a new project space for a Rails app:

mkdir rails4_integration_test

# set ruby version and gemset:
echo ruby-2.1.4 > rails4_integration_test/.ruby-version
echo rails4_integration_test > rails4_integration_test/.ruby-gemset

# install rails gem, and create a new project:
gem install rails --no-rdoc --no-ri
rails new .

# integrate with git
git init
git add .
git commit -am "initial commit"

# initialize database
rake db:migrate

For my test I created a new controller called "Demos" with an action "index":

rails generate controller Demos index

I revised the default index file (app/views/demos/index.html.erb) and added an ID to the H1 tag. Later in the code I will use a JQuery selector to find this element and get its content.

<h1 id='demos_index_test'>Demos#index</h1>
<p>Find me in app/views/demos/index.html.erb</p>

I wanted to integrate user authentication with this test, so I added the devise gem. edit file: Gemfile, added:

gem 'devise'

Devise integration:

# install gems:
bundle install

# execute devise generators:
rails generate devise:install
rails generate devise User

# update database:
rake db:migrate

Updated the Demos controller to require an authenticated user, edit file: app/controllers/demos_controller.rb

class DemosController < ApplicationController

  # require authenticated user:
  before_action :authenticate_user!

  def index
  end
end

Set the root/home route to point to the Demos controller index method. edit file: config/routes.rb, add:

root to: "demos#index"

Next I added the test gems. edit file: Gemfile, add:

group :test do
  gem 'rspec-rails'
  gem 'capybara'
  gem 'poltergeist'
  gem 'factory_girl_rails'
end

Install the gems and run the RSpec generator:

bundle install
rails generate rspec:install

Revised the RSpec helper to include the new gems and configuration, edit file: spec/spec_helper.rb

require 'rails_helper'

# ..snip..

require 'capybara/rspec'
require 'capybara/poltergeist'
require 'factory_girl_rails'

RSpec.configure do |config|

  # ..snip..

  # Factory Girl
  config.include FactoryGirl::Syntax::Methods

end

# register phantomjs driver:
Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, {
    debug: true,
    #timeout: 60,
    js_errors: true,
    #inspector: true,
  })
end

# set defaults:
Capybara.javascript_driver = :poltergeist
Capybara.default_driver = :poltergeist
# Capybara.default_wait_time = 60

Created a FactoryGirl User factory, new file: spec/factories/user.rb

FactoryGirl.define do
  factory :user do
    email "test@example.com"
    password "password123"
    password_confirmation "password123"
  end
end

Created the integration unit test, new file: spec/features/demos_spec.rb

describe "demos", :type => :feature, :js => true do

  before :all do
    @current_user = create(:user)
  end

  after :all do
    @current_user.destroy
  end

  it "logs in user" do

    # sign in
    visit new_user_session_path

    # fill out log in form
    fill_in 'user_email', with: @current_user.email
    fill_in 'user_password', with: @current_user.password
    find("#new_user input[type='submit']").click

    # execute jQuery selector to check page/H1 contents:
    result = page.evaluate_script("$(\"h1#demos_index_test\").text()")
    expect(result).to eq "Demos#index"

  end

end

The test can be executed via:

rspec
# or
rspec spec/features/demos_spec.rb

Example sucess output!

Finished in 2.44 seconds (files took 2.02 seconds to load)
1 example, 0 failures