Skip to content

Instantly share code, notes, and snippets.

@adriaanbd
Last active August 14, 2019 00:18
Show Gist options
  • Save adriaanbd/2b2a1f7f9d292ecc01583e88565286eb to your computer and use it in GitHub Desktop.
Save adriaanbd/2b2a1f7f9d292ecc01583e88565286eb to your computer and use it in GitHub Desktop.
RAILS RSPEC CAPYBARA PSQL CONFIG ROR

RSPEC CAPYBARA PSQL ROR CONFIG

A Ruby on Rails configuration for RSpec Capybara testing with a postgres database.

Create app

Postgres, and skip test files:

rails new capyspecpsql -d postgres -T

Modify files:

  • Gemfile
  • .rspec
  • spec_helper.rb
  • rails_helper.rb

Gemfile

group :development, :test do
  gem 'rspec-rails', '~> 3.8', '>= 3.8.2'
  gem 'faker', '~> 1.9', '>= 1.9.3'
  gem 'factory_bot_rails', '~> 5.0', '>= 5.0.2'
end

group :development do
  # rest ommitted
  gem 'spring-commands-rspec', '~> 1.0', '>= 1.0.4' # faster app boot time
end

group :test do 
  gem 'database_cleaner', '~> 1.7'
  gem 'capybara', '~> 3.22'
end

.rspec

--color
--require spec_helper  
--format doc

spec_helper.rb

  config.order = :random # runs test cases in random order
  Kernel.srand config.seed # to replicate exact test order 

rails_helper.rb

require 'database_cleaner' # eliminates dependencies by cleans database between specs
require 'capybara/rspec'
RSpec.configure do |config|
  # truncation prefered strategy with JS test cases
  config.use_transaction_fixtures = false
  # use truncation to clean before each suite:
  config.before(:suite) do 
    DatabaseCleaner.clean_with(:truncation)
  end
  # use truncation before each test for JS, transaction otherwise
  config.before(:each) do |example|
    DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
  end
  # clean db after each spec
  config.after(:each) do 
    DatabaseCleaner.clean 
  end
end

Troubleshooting

If the following warning occurs after executing rspec .

You must use Bundler 2 or greater with this lockfile.

Try the following command and run rspec . again: gem install bundler && bundler update --bundler

Modifying config/application.rb

config.generators do |g|
  g.test_framework :rspec,
    :fixtures => true, # generate a fixture for each model
    :view_specs => false,
    :helper_specs => false,
    # omits spec file for routes.rb:
    :routing_specs => false, # switch if app grows
    :controller_specs => true,
    :request_specs => true
  # generate factories instead of fixtures with target save dir
  g.fixture_replacement :factory_bot, :dir => "spec/factories"
 end

Examples

Quick version

$ rails g scaffold Album title:string
$ rails db:migrate RAILS_ENV=test
$ bundle exec spring binstub rspec
  • album_spec.rb
require 'rails_helper'

RSpec.describe Album do 
  #let(:album) {Album.new}
  subject { Album.new }

  it 'is not valid without a title' do
    expect(subject).not_to be_valid
  end

  it 'is not valid without a title longer than 100 chars' do 
    subject.title = 'a' * 101
    expect(subject).not_to be_valid
  end

  it 'is valid with proper data' do 
    subject.title = 'a' * 50
    expect(subject).to be_valid
  end
end
  • album.rb
class Album < ApplicationRecord
  validates :title, presence: true,
                    length: { maximum: 100 }
end

Run tests

$ rspec .

Longer version

$ rails g model Album title:string
$ rails db:migrate RAILS_ENV=test
$ bundle exec spring binstub rspec
  • album_spec.rb
require 'rails_helper'

RSpec.describe Album do 
  #let(:album) {Album.new}
  subject { Album.new }

  it 'is not valid without a title' do
    expect(subject).not_to be_valid
  end

  it 'is not valid without a title longer than 100 chars' do 
    subject.title = 'a' * 101
    expect(subject).not_to be_valid
  end

  it 'is valid with proper data' do 
    subject.title = 'a' * 50
    expect(subject).to be_valid
  end
end
  • album.rb
class Album < ApplicationRecord
  validates :title, presence: true,
                    length: { maximum: 100 }
end
$ rails db:migrate RAILS_ENV=test && rspec .
  • index_spec.rb
require 'rails_helper'

RSpec.feature 'Albums list' do # RSpec.describe '', type: :feature
  scenario 'unauthenticated user' do # alias for it method
    visit albums_path
    within '#content' do
      expect(find('h1')).to have_content('Albums')
    end
    # expect(find('#content h1')).to have_content('Albums')
    # to use xpath instead of css do: find(:xpath, 'foo')
  end
end
  • albums_controller.rb
class AlbumsController < ApplicationController
  def index
  end
end
  • app/views/albums/index.html.erb
<h1>Albums</h1>
  • app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Rorspec</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <div id="content">
      <%= yield %>
    </div>
  </body>
</html>
  • routes.rb
Rails.application.routes.draw do
  resources :albums, only: [:index]
  root to: 'albums#index'
end

$ rails rspec .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment