【问题标题】:Devise, Rspec, user expectations设计、Rspec、用户期望
【发布时间】:2011-04-22 02:51:05
【问题描述】:

我显然正在使用 Devise 进行身份验证,而我正在尝试做的是测试在用户对象上调用了一个方法。所以我的规范看起来像这样:

it "should retrieve something for user" do
  @user = Factory.create(:user) 
  sign_in @user
  @user.expects(:something)
  get :manage
end

我遇到的问题是期望失败,除非我这样做:

it "should retrieve something for user" do
  @user = Factory.create(:user) 
  sign_in @user
  controller.stubs(:current_user).returns @user
  @user.expects(:something)
  get :manage
end

如果 sign_in @user 是一个设计测试助手,那么在控制器上伪造 current_user 调用似乎是不明智的。在调试器中挖掘之后,当不伪造 current_user 时,@user 确实被返回,所以我不确定为什么没有达到预期。

想法?

【问题讨论】:

    标签: ruby-on-rails rspec devise


    【解决方案1】:

    当前用户在存储到会话之前被序列化;很可能,对于 ActiveRecord 用户模型,它正在存储用户的 id:

    https://github.com/plataformatec/devise/blob/master/lib/devise/test_helpers.rb#L49

    这意味着当你的控制器再次获取用户时:

    https://github.com/plataformatec/devise/blob/master/lib/devise/controllers/helpers.rb#L47-49

    它在会话中查找 user_id,并从数据库中重新获取用户。

    这意味着控制器中#current_user 返回的用户对象与您在测试中传递给#sign_in 的@user 对象是不同的Ruby 对象,因此一个对象的存根和期望不会附加到另一个对象。

    我没有广泛使用过 Devise/Warden,但我很确定这就是正在发生的事情。您可以尝试在两个实例上打印 #object_id 来确认这一点。

    2014 年 2 月的挑剔语义更新:

    在您的控制器上存根 #current_user 是一种很好的方法,具体取决于您对被测系统 (SUT) 的“边界”的定义——即您的控制器。假设 #current_user 方法由 Devise 定义并由 Devise 混合到您的控制器中,您可能会认为 #current_user 在您的 SUT 外部,因此对于存根来说是公平的游戏。

    您也可以存根 Devise 正在访问的底层 (ActiveRecord),例如存根 User.find 以返回您的 @user 对象。这意味着您的规范正在测试您的操作实现以及 #current_user 的设计实现,因此如果这些内容中的任何一个稍后发生更改,规范都会失败。

    假设 Devise 使用 User.find(args),并假设您存根该方法,然后更高版本的 Devise 更改为使用 User.where(args).first() - 您的代码没有更改,但底层库已更改,并且您的规范失败。非常普遍地考虑这个想法,有时您可能会喜欢这种行为(考虑使用例如 WebMock 模拟原始 HTTP 响应而不是存根 Net::HTTP 方法,以便您以后可以交换 http 库),有时您不会(也许这个设计问题算作一个)。

    【讨论】:

    • 昨晚经过一番挖掘,看来你是对的!感谢您的信息。
    • 经过数小时的搜索终于找到了解决方案。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多