One of the Rails apps I work on has this:
$ rspec spec #... Finished in 61.82 seconds 475 examples, 0 failures
61 seconds! (And on top of that I have to wait another 15 seconds for Rails load; that’s a whole other story, and I hope to come back to that in a future post.) A quick dig into the 61 seconds reveals this:
$ rspec spec/controllers #... Finished in 22.82 seconds 114 examples, 0 failures
It happens that almost every controller action in our app needs the user to be logged in, so every controller spec needs that too. We’re using Devise, so every spec has something equivalent to this:
before :each do sign_in Factory(:user) end
This implies hits on the database to create the user object, record the IP address and last sign-in time, etc etc. We can do much better by using a mock_model for the User, and stubbing out Warden‘s authentication completely.
First, in the interests of DRYness, I made some helper methods to do some of the jobs that factory_girl does:
module RandomHelpers def random_number() SecureRandom.random_number(10000); end def random_id() random_number; end def random_name() SecureRandom.hex(20); end end
Next, a method that creates a test double representing the User who will be logged in:
module ControllerHelpers
def user_double(attrs = {})
user_attrs = {
:first_name => random_name,
:last_name => random_name,
:authenticatable_salt => 'x'
}.merge(attrs)
mock_model(User, user_attrs)
end
end
This is the place to fill the User double with default attribute values. Our app requires every User to have a first and last name, so I’ve stubbed these with random strings; they can be overridden by the caller if specific values are needed for any test. In your own app, change this method to set any mandatory attributes your user model needs. Note also the stubbed authenticatable_salt() method, which wasn’t required when mocking earlier versions of Devise.
I also need a method that logs a user in:
module ControllerHelpers
def stub_sign_in_with(user)
request.env['warden'] = double(Warden,
:authenticate => user,
:authenticate! => user,
:authenticate? => true)
sign_in(user)
return user
end
end
This replaces Warden with a test double, and Devise is none the wiser.
I also create a method to glue these together for the simplest (and most common) case:
module ControllerHelpers
def stub_sign_in(attrs = {})
stub_sign_in_with user_double(attrs)
end
end
(Note that this returns the User double; you’ll see why that’s useful in a moment.)
Finally, I configure the specs so they have access to these helper methods. To achieve that, spec/spec_helper.rb needs to look something like this:
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include RandomHelpers
config.include Devise::TestHelpers, :type => :controller
config.include ControllerHelpers, :type => :controller
end
Putting all this together allows me to write controller specs with:
before do stub_sign_in end
or, if I need access to the user double:
let(:user) { stub_sign_in }
And the payoff?
$ rspec spec/controllers #... Finished in 7.36 seconds 114 examples, 0 failures
I saved as whole 15 seconds! There’s still some work to do in my specs, but stubbing out Warden has made a massive difference to every test run that involves this project’s controller microtests.
Note that the above has been tested only with the following gem versions:
- rails 3.0.3
- devise 1.3.4
- warden 1.0.4
- rspec-core 2.6.4
- rspec-mocks 2.6.0
- rspec-rails 2.6.1
- factory_girl 1.3.3
A condensed gist for the above code is here.





Edward Robertshaw
August 7, 2011
Have you looked at spork? It maintains a server in the background for the tests to run on so when you press go on the tests you dont have to wait for rails to fire up.
https://github.com/timcharper/spork
Ed
Kevin Rutherford
August 8, 2011
@ed – yes, we used spork (and guard) for a while. They’re great tools, but they solve the wrong problem. See Why I don’t use Spork for my rationale here.
rapindRapin
August 23, 2011
Gold. I’ll be using this in one of my projects. Thanks.
Evan R. Murphy
March 16, 2012
This issue of Devise::TestHelpers’ sign_in requiring database interaction was troubling me so I’ve been searching around. That’s how I found this article – which is good. But I was also pleasantly surprised to find a Devise-endorsed article on the subject: https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs The implementation is similar to yours but not exactly the same.
Kevin Rutherford
March 16, 2012
Yes, and Devise itself has moved on somewhat too.
Evan R. Murphy
March 16, 2012
In what way?