3

当我试图让 Devise 在我的控制器测试中正确登录时,我有一些有趣的行为。它似乎在某些情况下有效,但在其他情况下无效。我不确定这是 Devise 和 FactoryGirl 之间的交互还是其他工作。

首先,这是我的工厂:

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

我的控制器:

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

和我的控制器测试:

 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

login_user 规范助手是它变得时髦的地方。如果我取消注释下面的行,强制将用户设置为 FactoryGirl 对象,则测试通过。如果我留下评论,Devise 会尝试以传递的用户身份登录(我已通过调试验证是数据库中的同一用户),但它实际上并没有登录。在这两种情况下,sign_in 调用实际上返回相同的数组,但基于执行路径,控制器代码永远不会执行,因为 Devise 重定向到登录页面。

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

如何让 sign_in 正常工作?

作为记录,当涉及到 Rails 的 TDD 时,我花了 10 分钟让我的实际代码正常工作,并花 2 小时跳过障碍来让我的测试代码完成它应该做的事情。

4

1 回答 1

4

我最近开始尝试接受 TDD 的哲学,并承认我和你一开始有同样的感觉。您的时间估计似乎相当准确,10 分钟的开发和 2 小时的测试用例实施。我的第一个建议是,就像生活中的许多事情一样,它会变得更好。当你第一次做出看似无害的改变然后意识到你已经打破了一半的测试回归时,你会很高兴你吃了药。

话虽如此,这听起来像是一个警察,因为你在问为什么Devise不起作用,我的回答是:你不应该关心。显然你做错了什么,恐怕我无法从所提供的信息中分辨出它是什么,但我想我无论如何都能提供帮助。

我在上面看到的唯一错误是您的规范正在测试至少四件事:

  1. 响应是重定向。
  2. @client被安排了。
  3. AClient被破坏。
  4. Devise正在提供适当的身份验证。

规范应该只测试一件事和一件事。尽管进行更多测试可能很诱人,但我不推荐它。Cucumber 或其他集成测试测试一堆东西,但不是规范。

Devise不是你应该在这里测试的东西,所以stub它出来了。我认为这样的事情会起作用:

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

在此之后,为您正在测试的三件事创建三个不同it "should" do的块。

另一个提示是我认为您不需要association在定义FactoryGirl factory. 我认为这只需要多态关联。通常,您可以只给出不带值的关联名称,它将以相同的名称运行工厂。请注意无限循环。

我希望这会有所帮助。

于 2013-01-27T06:16:52.477 回答