Posts

Devise: Testing controllers and requests that need authentication with rspec2

Montag, 04. April 2011, 01:07 Uhr | roberto@vasquez-angel.de |

Imagine you have been happily writing your controller test and then, you have to add authentication to your application using devise.

Assume you have a posts controller with following test:

require 'spec_helper'

describe PostsController do
  def mock_post(stubs={})
    @mock_post ||= mock_model(Post, stubs).as_null_object
  end

  describe "GET index" do
    it "assigns all posts as @posts" do
      Post.stub(:all) { [mock_post] }
      get :index
      assigns(:posts).should eq([mock_post])
    end
  end

  describe "GET show" do
    it "assigns the requested post as @post" do
      Post.stub(:find).with("37") { mock_post }
      get :show, :id => "37"
      assigns(:post).should be(mock_post)
    end
  end
end  

First, you’ll have to add the Devise TestHelpers. Add a new file spec/support/devise.rb:

RSpec.configure do |config|
  config.include Devise::TestHelpers, :type => :controller
end

You’ll need a factory for the admin. I use factory girl. Add the admin factory to Rails.root/spec/factories.rb:

Factory.define(:admin) do |admin|
  admin.email    "admin@example.com"
  admin.password "foobar"
end

Then you throw all your controller tests into a new describe block, that signins in your admin before each test:

describe PostsController do
  describe "as a signed in admin" do
    before(:each) do
      admin = Factory(:admin)
      sign_in admin
    end
    
    def mock_post(stubs={})
      @mock_post ||= mock_model(Post, stubs).as_null_object
    end

    describe "GET index" do
      it "assigns all posts as @posts" do
        Post.stub(:all) { [mock_post] }
        get :index
        assigns(:posts).should eq([mock_post])
      end
    end

    describe "GET show" do
      it "assigns the requested post as @post" do
        Post.stub(:find).with("37") { mock_post }
        get :show, :id => "37"
        assigns(:post).should be(mock_post)
      end
    end
  end
end

Customize the request specs. Before:

require 'spec_helper'

describe "NewsItems" do
  describe "GET /news_items" do
    it "works! (now write some real specs)" do
      # Run the generator again with the --webrat flag if you want to use webrat methods/matchers
      get news_items_path
      response.status.should be(200)
    end
  end
end

and with devise integration:

require 'spec_helper'

describe "NewsItems" do
  context "as logged in admin" do
    before(:each) do
      admin = Factory(:admin)
      get new_admin_session_path
      fill_in :email,    :with => admin.email
      fill_in :password, :with => admin.password
      click_button
    end
    
    describe "GET /news_items" do
      it "works! (now write some real specs)" do
        # Run the generator again with the --webrat flag if you want to use webrat methods/matchers
        get news_items_path
        response.status.should be(200)
      end
    end
  end
end

Rake task to create a new devise user

Montag, 04. April 2011, 01:07 Uhr | roberto@vasquez-angel.de |

namespace :devise do
  desc "Create admin"
  task :create_admin, :email, :password, :needs => :environment do |t, args|
    if a = Admin.create(:email => args.email, :password => args.password)
      puts "Created admin"
    else 
      puts a.errors
    end    
  end
end  

Usage:

$> rake devise:create_admin["user@example.com","password"]

Add a new role to your site with devise

Montag, 04. April 2011, 01:06 Uhr | roberto@vasquez-angel.de |

Adding a new role (i.e. “Admin”) is easy:

$> rails generate devise Admin

Don’t forget to migrate:

$> rake db:migrate

Then you can check for the admin in your controllers:

class BackendController < ApplicationController
  before_filter :authenticate_admin!
end 

Installing Devise

Montag, 04. April 2011, 01:06 Uhr | roberto@vasquez-angel.de |

  • Add the gem to your Gemfile:
gem 'devise'
  • Install your Bundle:
$> bundle install
  • Run the generator:
$> rails generate devise:install

This will generate a initialiter in your config/initializers folder. You should check the options in config/initializers/devise.rb

  • Add default URL options to your mailer configuration in the environments files:

environments/development.rb:

config.action_mailer.default_url_options = { :host => 'localhost:3000' }

environments/production.rb:

config.action_mailer.default_url_options = { :host => 'blog.robotex.de' }