Integration testing a Rails/React app with RSpec

I’ve been playing around with React lately.

I built a Rails API backed app as a learning exercise, the app is a Todo List, but not the usual TodoMVC. Think multiple users with multiple lists of items. I felt that would be enough exposure into the React way of doing things.

Well, actually since I like to torture myself when learning new things, I started out doing this in Relay, including building the GraphQL server using graphql-ruby.

Next stop was doing it in Vanilla React, which felt horribly painful after all the magick that Relay brings to the table.

After that came doing it again with Redux, I figured it made sense to expose myself to more than one way of doing things in React.

Once I got it all running halfway decently I wanted to try and get RSpec integration tests up and running. After much searching and finding random tidbits that worked here and there, once I got a working setup I figured I’d document it. This setup will save a screenshot on failure and open it in the appropriate app (I’ve only tested this bit on macOS).

You will need to install chromedriver, you can get it here or install it via homebrew.

Here are the relevant files for a working (as of 2018-02-26) RSpec/React integration test setup.

Gemfile

...

group :test do
  gem 'capybara', '~> 2.4.4'
  gem 'capybara-screenshot', '~> 1.0.11'
  gem 'database_cleaner'
  gem 'rspec-rails'
  gem 'selenium-webdriver'
end

Procfile-react-test

web: cd clients/react && yarn start
api: RAILS_ENV=test bundle exec rails s -p 3001

spec/rails_helper.rb (add this bit after require 'rspec/rails')

Dir['spec/support/**/*.rb'].each do |file|
  require Rails.root.join(file).to_s
end

spec/support/capybara.rb

require 'capybara/rspec'
require 'capybara/rails'
require 'capybara-screenshot/rspec'

Capybara.app_host = "http://localhost:3000"
Capybara.default_wait_time = 5

Capybara::Screenshot.webkit_options = {
  width: 1024,
  height: 768
}

Capybara::Screenshot.autosave_on_failure = false
Capybara::Screenshot.prune_strategy = :keep_last_run

RSpec.configure do |config|
  config.after do |example|
    if example.metadata[:type] == :feature and example.metadata[:js] and example.exception.present?
      Capybara::Screenshot.screenshot_and_open_image
    end
  end
end

spec/support/capybara_drivers/selenium.rb (I have multiple files as I was testing different drivers, that’s also the reason for catching LoadError)

begin
  require 'selenium/webdriver'
  require 'capybara/rspec'

  Capybara.register_driver :selenium do |app|
    options = Selenium::WebDriver::Chrome::Options.new(
      args: %w[headless disable-gpu no-sandbox]
    )
    Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
  end

  Capybara.javascript_driver = :selenium
rescue LoadError
end

spec/features/signups_spec.rb

require 'rails_helper'

RSpec.feature "Signups", type: :feature, js: true do

  scenario "Visitor signs up for a new account" do
    visit("/signup")
    fill_in "name", with: "Bob"
    fill_in "email", with: "bob@example.com"
    fill_in "password", with: "password"
    click_button "Create Account"
    expect(page).to have_text("Logout Bob")
  end

end

To run the tests you need to start the server and client.

foreman start -p 3000 -f Procfile-react-test

And run your feature specs

% bundle exec rspec spec/features
.

Finished in 4.98 seconds (files took 9.46 seconds to load)
1 example, 0 failures

And there you have it, those are the changes you need to get this working.