Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I simulate a login with RSpec?

I have been playing with Rails for a couple of years now and have produced a couple of passable apps that are in production. I've always avoided doing any testing though and I have decided to rectify that. I'm trying to write some tests for an app that I wrote for work that is already up and running but undergoing constant revision. I'm concerned that any changes will break things so I want to get some tests up and running. I've read the RSpec book, watched a few screencasts but am struggling to get started (it strikes me as the sort of thing you only understand once you've actually done it).

I'm trying to write what should be a simple test of my ReportsController. The problem with my app is that pretty much the entire thing sits behind an authentication layer. Nothing works if you're not logged in so I have to simulate a login before I can even send forth a simple get request (although I guess I should write some tests to make sure that nothing works without a login - I'll get to that later).

I've set up a testing environment with RSpec, Capybara, FactoryGirl and Guard (wasn't sure which tools to use so used Railscasts' suggestions). The way I've gone about writing my test so far is to create a user in FactoryGirl like so;

FactoryGirl.define do   sequence(:email) {|n| "user#{n}@example.com"}   sequence(:login) {|n| "user#{n}"}   factory :user do     email {FactoryGirl.generate :email}     login {FactoryGirl.generate :login}     password "abc"     admin false     first_name "Bob"     last_name "Bobson"   end end 

and then write my test like so;

require 'spec_helper'  describe ReportsController do   describe "GET 'index'" do     it "should be successful" do       user = Factory(:user)       visit login_path       fill_in "login", :with => user.login       fill_in "password", :with => user.password       click_button "Log in"       get 'index'       response.should be_success     end   end end 

This fails like so;

  1) ReportsController GET 'index' should be successful      Failure/Error: response.should be_success        expected success? to return true, got false      # ./spec/controllers/reports_controller_spec.rb:13:in `block (3 levels) in <top (required)>' 

Interestingly if I change my test to response.should be_redirect, the test passes which suggests to me that everything is working up until that point but the login is not being recognised.

So my question is what do I have to do to make this login work. Do I need to create a user in the database that matches the FactoryGirl credentials? If so, what is the point of FactoryGirl here (and should I even be using it)? How do I go about creating this fake user in the testing environment? My authentication system is a very simple self-made one (based on Railscasts episode 250). This logging in behaviour will presumably have to replicated for almost all of my tests so how do I go about doing it once in my code and having it apply everywhere?

I realise this is a big question so I thank you for having a look.

like image 257
brad Avatar asked Apr 12 '12 10:04

brad


People also ask

How do I run an RSpec test?

Running tests by their file or directory names is the most familiar way to run tests with RSpec. RSpec can take a file name or directory name and run the file or the contents of the directory. So you can do: rspec spec/jobs to run the tests found in the jobs directory.

What is let in RSpec?

let generates a method whose return value is memoized after the first call. This is known as lazy loading because the value is not loaded into memory until the method is called. Here is an example of how let is used within an RSpec test. let will generate a method called thing which returns a new instance of Thing .

Is RSpec TDD or BDD?

RSpec is a Behavior-Driven Development tool for Ruby programmers. BDD is an approach to software development that combines Test-Driven Development, Domain Driven Design and Acceptance Test-Driven Planning. RSpec helps you do the TDD part of that equation, focusing on the documentation and design aspects of TDD.

How do I set up RSpec?

Installing RSpecBoot up your terminal and punch in gem install rspec to install RSpec. Once that's done, you can verify your version of RSpec with rspec --version , which will output the current version of each of the packaged gems. Take a minute also to hit rspec --help and look through the various options available.


2 Answers

The answer depends on your authentication implementation. Normally, when a user logs in, you'll set a session variable to remember that user, something like session[:user_id]. Your controllers will check for a login in a before_filter and redirect if no such session variable exists. I assume you're already doing something like this.

To get this working in your tests, you have to manually insert the user information into the session. Here's part of what we use at work:

# spec/support/spec_test_helper.rb module SpecTestHelper      def login_admin     login(:admin)   end    def login(user)     user = User.where(:login => user.to_s).first if user.is_a?(Symbol)     request.session[:user] = user.id   end    def current_user     User.find(request.session[:user])   end end  # spec/spec_helper.rb RSpec.configure do |config|   config.include SpecTestHelper, :type => :controller end 

Now in any of our controller examples, we can call login(some_user) to simulate logging in as that user.


I should also mention that it looks like you're doing integration testing in this controller test. As a rule, your controller tests should only be simulating requests to individual controller actions, like:

it 'should be successful' do   get :index   response.should be_success end 

This specifically tests a single controller action, which is what you want in a set of controller tests. Then you can use Capybara/Cucumber for end-to-end integration testing of forms, views, and controllers.

like image 128
Brandan Avatar answered Oct 13 '22 15:10

Brandan


Add helper file in spec/support/controller_helpers.rb and copy content below

module ControllerHelpers     def sign_in(user)       if user.nil?         allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})         allow(controller).to receive(:current_user).and_return(nil)       else         allow(request.env['warden']).to receive(:authenticate!).and_return(user)         allow(controller).to receive(:current_user).and_return(user)       end     end   end 

Now add following lines in spec/rails_helper.rb or spec/spec_helper.rb file

require 'support/controller_helpers'  RSpec.configure do |config|      config.include Devise::TestHelpers, :type => :controller     config.include ControllerHelpers, :type => :controller    end 

Now in your controller spec file.

describe  "GET #index" do      before :each do                 @user=create(:user)         sign_in @user     end       ... end 

Devise Official Link

like image 33
Taimoor Changaiz Avatar answered Oct 13 '22 16:10

Taimoor Changaiz