Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Sessions in Rails 3 with Rspec & Capybara

I'm trying to write integration tests with rspec, factory_girl & capybara. I also have cucumber installed, but I'm not using it (to my knowledge).

I basically want to prepopulate the db with my user, then go to my home page and try to log in. It should redirect to user_path(@user).

However, sessions don't seem to be persisted in my /rspec/requests/ integration tests.

My spec: /rspec/requests/users_spec.rb

require 'spec_helper'

describe "User flow" do

  before(:each) do
    @user = Factory(:user)
  end

  it "should login user" do

    visit("/index")

    fill_in :email, :with => @user.email
    fill_in :password, :with => @user.password
    click_button "Login"
    assert current_path == user_path(@user)
  end
end

Returns:

Failures:

  1) User flow should login user
     Failure/Error: assert current_path == user_path(@user)
     <false> is not true.
     # (eval):2:in `send'
     # (eval):2:in `assert'
     # ./spec/requests/users_spec.rb:16

Instead, it redirects to my please_login_path - which should happen if the login fails for any reason (or if session[:user_id] is not set).

If I try to put session.inspect, it fails as a nil object.

If I try to do this in the controller tests (/rspec/controllers/sessions_spec.rb), I can access the session with no problem, and I can call session[:user_id]

like image 773
Tom Blomfield Avatar asked Mar 08 '11 16:03

Tom Blomfield


2 Answers

If you are using Devise, you'll need to include Warden::Test::Helpers (right after the require of spec_helper is a good place) as outlined in the warden wiki.

The call to session is returning nil because capybara doesn't provide access to it when running as an integration test.

like image 93
Benjamin Curtis Avatar answered Sep 19 '22 09:09

Benjamin Curtis


I have the same problems and although filling out a form might be an option for some, I had to roll my own authentication ruby because I was using a third party auth system (Janrain to be exact).... in my tests I ended up using something like this:

Here is what I have in my spec/support/test_helpers_and_stuff.rb

module AuthTestHelper

  class SessionBackdoorController < ::ApplicationController
    def create
      sign_in User.find(params[:user_id])
      head :ok
    end
  end

  begin
    _routes = Rails.application.routes
    _routes.disable_clear_and_finalize = true
    _routes.clear!
    Rails.application.routes_reloader.paths.each{ |path| load(path) }
    _routes.draw do
      # here you can add any route you want
      match "/test_login_backdoor", to: "session_backdoor#create"
    end
    ActiveSupport.on_load(:action_controller) { _routes.finalize! }
  ensure
    _routes.disable_clear_and_finalize = false
  end

  def request_signin_as(user)
    visit "/test_login_backdoor?user_id=#{user.id}"
  end

  def signin_as(user)
    session[:session_user] = user.id
  end

end

Then in my request spec, with capybara and selenium, I did the following:

describe "Giveaway Promotion" do
  context "Story: A fan participates in a giveaway", js: :selenium do
    context "as a signed in user" do

      before :each do
        @user = Factory(:user)
        request_signin_as @user
      end

      it "should be able to participate as an already signed in user" do
        visit giveaway_path
        ....
      end
    end
  end
end

BTW, I came up with solutions after trying the proposed solutions to this post and this post and neither of them worked for me. (but they certainly inspired my solution)

Good luck!

like image 38
PeppyHeppy Avatar answered Sep 22 '22 09:09

PeppyHeppy