I’ve got some funny behavior when trying to get Devise to sign in properly in my controller testing. It seems to work in certain cases, but not in others. I’m not sure if this is an interaction between Devise and FactoryGirl or something else at work.
First off, here’s my factories:
factory :advisor do
name "Jason Jones"
association :user
initialize_with {Advisor.find_or_create_by_name('Jason Jones')}
end
factory :client do
name "Rich Homeowner"
association :advisor
end
factory :user do
email "jason@jones.com"
password "testpassword"
initialize_with {User.find_or_create_by_email('jason@jones.com')}
end
my controller:
class ClientsController < ApplicationController
before_filter :authenticate_user!
def destroy
@client = current_user.advisor.clients.where(:id => params[:id]).first
@client.destroy
flash[:notice] = 'Client deleted.'
redirect_to clients_path
end
and my controller test:
describe "DELETE destroy" do
it "should delete a client" do
a = FactoryGirl.create(:advisor)
c = FactoryGirl.create(:client, :advisor => a)
login_user(a.user)
expect{
delete :destroy, :id => c.id
response.should be_redirect
assigns(:client).should eq(c)
}.to change(Client, :count).by(-1)
end
end
the login_user spec helper is where it gets funky. if I uncomment the line below, forcing the user to be set to the FactoryGirl object, the test passes. If I leave it commented, Devise attempts to sign in as the passed user (which I have verified via debugging is the same user in the DB), but it does not actually sign in. The sign_in call actually returns the same array in both cases, but based on following the execution path, the controller code is never executed, because Devise redirects to the login page.
def login_user(user=nil)
@request.env["devise.mapping"] = Devise.mappings[:user]
if user.nil?
user = FactoryGirl.create(:user)
end
# user = FactoryGirl.create(:user) # uncommenting this line causes test to pass
sign_in user
end
How do I get the sign_in to work properly?
For the record, when it comes to TDD for Rails, I spend 10 min getting my actual code to work properly and 2 hours jumping through hoops to get my test code to do what it’s supposed to.
I recently started trying to swallow the philosophy of TDD and admit that I had the same exact feeling as you did at first. Your time estimates seem pretty accurate, 10 minutes of development and 2 hours of test case implementation. My first advice is, like many things in life, it gets better. The first time you make a seemingly innocuous change then realize that you’ve broken half your test regression you’ll be glad you took the pill.
With that said, this is going to sound like a cop out because you’re asking why
Devisedoesn’t work and my answer is: you shouldn’t care. Clearly you’re doing something wrong, and I’m afraid I can’t tell what it is from the information given, but I think I can help anyway.The only thing I see wrong above is that your spec is testing at least four things:
@clientis assigned.Clientis destroyed.Deviseis providing proper authentication.A spec should test one thing and one thing only. As tempting as it may be to test more, I don’t recommend it. Cucumber or other integration tests test bunches of things, but not specs.
Deviseis not something you should test here, sostubit out. I think something like this will work:After this, create three different
it "should" doblocks for the three things you’re testing.Another tip is I don’t think you need to specify
associationwhen defining aFactoryGirlfactory. I think this is only needed for polymorphic associations. Normally you can just give the name of the association without a value and it will run the factory with the same name. Just watch out for infinite loops.I hope that helps.