设计,Rspec,用户期望

时间:2011-04-22 02:51:05

标签: ruby-on-rails rspec devise

我正在使用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是设备测试帮助器,那么<​​p>伪造控制器上的current_user调用似乎很有用。在挖掘调试器之后,当没有伪造出current_user时,@ user实际上正在返回,所以我不确定为什么没有满足期望。

想法?

1 个答案:

答案 0 :(得分:3)

当前用户在存储到会话之前被序列化;可能,对于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返回的User对象是与您在测试中传入#sign_in的@user不同的Ruby对象,因此来自一个对象的存根和期望不会附加到另一个对象。 / p>

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

Picky语义更新2014年2月:

根据您对被测系统(SUT)“边界”的定义 - 即您的控制器,在控制器上对#current_user进行存储是一种很好的方法。假设#current_user方法由Devise定义并由Devise混合到您的控制器中,您可以认为#current_user在您的SUT外部,因此可以公平地进行存根游戏。

您也可以存根Devise正在访问的底层(ActiveRecord),比如通过存根User.find来返回您的@user对象。这意味着您的规范正在测试您的操作实现以及#current_user的Devise实现,因此如果以后更改这些内容,规范将会失败。

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