Skip to content

Step09: Setting Up the Testing Environment

Lev Brie edited this page Jul 30, 2013 · 8 revisions

Note: You should already have rspec installed at this point Inspired by Ryan Bates' Railscasts on Testing - #275 and #413

  1. Make sure you first run $ rake db:test:prepare if you haven't already. Otherwise, the tests will not be able to access the database. Also run $ bundle binstubs rspec-core to create a binstub file to use to run our model specs.

  2. Initialize Guard for rspec: $ guard init rspec. This adds rspec-guard to the guardfile for watching changes to our specs.

  3. In a new tab, run $ guard to start up guard.

  4. Enable capybara in rspec by requiring it at the top of spec/spec_helper.rb: require 'capybara/rspec'.

  5. Remove the fixture path since we won't be using it:

Remove this line if you're not using ActiveRecord or ActiveRecord fixtures

config.fixture_path = "#{::Rails.root}/spec/fixtures" ```

  1. You should already have a factories/ directory inside of spec/ and a users.rb file inside of that. If you don't, create these now (you must have Factory Girl installed). Inside of spec/factories/users.rb create a User factory as follows:

    FactoryGirl.define do
      factory :user do
      	name 'Test User'
      	email 'test@example.com'
      	password 'password55'
      	password_confirmation 'password55'
      	confirmed_at Time.now 				# use only if implementing Confirmable
      end
    end
  2. Configure Devise Test Helpers in spec_helper.rb inside of the Rspec.configure do |config| block with config.include Devise::TestHelpers, :type => :controller see here for more info.

  3. Also add database cleaners to the same file to ensure a clean slate during tests (See the database_cleaner gem for more info):

config.before(:suite) do DatabaseCleaner.strategy = :truncation end config.before(:each) do DatabaseCleaner.start end config.after(:each) do DatabaseCleaner.clean end ```

  1. Create a controller_macros.rb file inside of spec/support and add the following:

module ControllerMacros def login_user before(:each) do @request.env["devise.mapping"] = Devise.mappings[:user] user = FactoryGirl.create(:user) sign_in user end end end ```

  1. Add your new Controller Macro to spec_helpers.rb just under your Devise Test Helpers configuration: config.extend ControllerMacros, :type => :controller

  2. To test our Devise Test Helpers we'll need to create a users controller: $ rails g controller users index show --skip-stylesheets --skip-javascripts and add before_filter :authenticate_user! to users_controller.rb.

  3. Now we can create our first set of tests in spec/controllers/users_controller_spec.rb:

require 'spec_helper'

describe UsersController do login_user

it "should have a current_user" do subject.current_user.should_not be_nil end

it "should get index" do # Note, erails 3.x scaffolding may add lines like get :index, {}, valid_session # the valid_session overrides the devise login. Remove the valid_session from your specs get 'index' response.should be_success end end ```

These tests should be passing (check that this is the case in your guard tab in terminal).

  1. Next, we add tests for the User model in spec/models/user_spec.rb. These tests are largely taken from Daniel Kehoe's excellent RailsApps example application using Devise, CanCan, and Bootstrap.

require 'spec_helper'

describe User do before(:each) do @user_attrs = { :name => "Example User", :email => "user@example.com", :password => "password55", :password_confirmation => "password55", :confirmed_at => Time.now } end

it "should create a new user given a valid attribute" do User.create!(@user_attrs) end

it "should require an email address" do blank_email_user = User.new(@user_attrs.merge(:email => "")) blank_email_user.should_not be_valid end

it "should accept valid email addresses" do addresses = %w[user@foo.com THE_USER@foo.bar.org first.last@foo.jp] addresses.each do |address| valid_email_user = User.new(@user_attrs.merge(:email => address)) valid_email_user.should be_valid end end

it "should reject invalid email addresses" do addresses = %w[user@foo,com user_at_foo.org example.user@foo.] addresses.each do |address| invalid_email_user = User.new(@user_attrs.merge(:email => address)) invalid_email_user.should_not be_valid end end

it "should reject duplicate email addresses" do User.create!(@user_attrs) user_with_duplicate_email = User.new(@user_attrs) user_with_duplicate_email.should_not be_valid end

it "should reject email addresses identical up to case" do upcased_email = @user_attrs[:email].upcase User.create!(@user_attrs.merge(:email => upcased_email)) user_with_duplicate_email = User.new(@user_attrs) user_with_duplicate_email.should_not be_valid end

describe "passwords" do it "should have a password and password_confirmation attributes" do @user = User.new(@user_attrs) @user.should respond_to(:password) @user.should respond_to(:password_confirmation) end end

describe "password validations" do it "should require a password" do User.new(@user_attrs.merge(:password => "", :password_confirmation => "")). should_not be_valid end

it "should require a matching password confirmation" do
  User.new(@user_attrs.merge(:password_confirmation => "invalid")).
    should_not be_valid
end

it "should reject short passwords" do
  short = "a" * 5
  hash = @user_attrs.merge(:password => short, :password_confirmation => short)
  User.new(hash).should_not be_valid
end

end

describe "password encryption" do before(:each) do @user = User.create!(@user_attrs) end

it "should have an encrypted password attribute" do
  @user.should respond_to(:encrypted_password)
end

it "should set the encrypted password attribute" do
  @user.encrypted_password.should_not be_blank
end

end end ```

  1. Finally, we're ready for our first test-driven test. Add the following to spec/controllers/users_controller_spec.rb:

it "should not get index" do subject.current_user.destroy get 'index' response.should_not be_success end ```

It should not be passing! This is because we aren't actually authorizing users yet. Anyone can access any page. In the next section, we'll add in this functionality using CanCan and Rolify.

Clone this wiki locally