Devise will not enter controller test correctly with FactoryGirl

I have a funny behavior when trying to get Devise to log into my controller correctly. 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 of all, here are 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

      

helper helper login_user 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 his comment, Devise will try to log in as the past user (which I checked through debugging is the same user in the DB), but he is not actually logged. The sign_in call actually returns the same array in both cases, but based on the following execution path, the controller code is never executed because Devise is redirected 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 sign_in to work correctly?

For the record, when it comes to TDD for Rails, I spend 10 minutes getting my actual code to work correctly and 2 hours later jumping through hoops to get my test code to do what it should.

+3


source to share


1 answer


I recently started trying to internalize the philosophy of TDD and admit that I had the same feeling as you. Your time estimates seem pretty accurate, 10 minutes of development and 2 hours of test case implementation. My first advice, like so many things in life, is getting better. The first time you make a seemingly harmless change, then realize that you've broken half of your test regression, you'll be glad you took the pill.

With that said, it will sound like a cop because you ask why it Devise

doesn't work and my answer is: you don't care. It is clear that you are doing something wrong and I am afraid I cannot tell you what this is from the information provided, but I think I can help anyway.

The only thing I see wrong is that your spec is testing at least four things:

  • The answer is a redirect.
  • @client

    ...
  • A is Client

    destroyed.
  • Devise

    provides proper authentication.

The spec should only check one thing and only one thing. As tempting as it may be to check out more, I don't recommend it. Cucumbers or other integration tests check bundles of things, but not specs.



Devise

- this is not something you should check here, therefore stub

. I think something like this will work:

before :each do
  @advisor = FactoryGirl.create(:advisor)
  controller.stub(:authenticate_user!).and_return(true)
  controller.stub(:current_user).and_return(@advisor.user)
end

      

After that, create three different blocks it "should" do

for the three objects you are testing.

One more tip: I don't think you need to specify association

when defining FactoryGirl

factory

. I think this is only needed for polymorphic associations. Usually you can just give the name to the association with no value and it will run a factory with the same name. Just watch out for endless loops.

I hope this helps.

+4


source







All Articles