如何在 Rspec/Capybara 测试中访问 FactoryGirl 生成的用户的虚拟属性?

How do I access a virtual attribute on a FactoryGirl-generated user in an Rspec/Capybara test?

在自学 Ruby 和 Rails 的过程中,我目前正在构建一个紧密基于 Hartl tutorial 的用户系统;此时的主要区别是我使用 Rspec/Capybara 而不是 TestUnit 对其进行测试。到目前为止,大多数事情都相当简单,但我一直在测试密码重置时遇到这个问题。

当我手动测试密码重置时,它们完全按预期工作。因此,我很确定问题出在 Rspec、Capybara 和 FactoryGirl 之间。

一直失败的规范代码是当我想访问忘记密码的时候link:

# password_resets_spec.rb
visit(edit_password_reset_path(spec_user.reset_token, email: spec_user.email))

它给了我:

ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"password_resets", :email=>"john2@example.com", :format=>nil, :id=>nil} missing required keys: [:id]

密码重置是一个(部分)资源,它们的 id 是用户的 reset_token

spec_user由FactoryGirl生成:

#password_resets_spec.rb
let(:spec_user){ FactoryGirl.create :user }

相关的 :user 工厂没有设置 reset_token 但是当我用一个设置的工厂尝试它时,重置表单没有正确设置令牌,并且与生成的摘要。这是设置两者的代码:

# user.rb
def create_reset_digest
  self.reset_token = User.new_token
  update_attribute(:reset_digest, User.digest(reset_token))
  update_attribute(:reset_sent_at, Time.zone.now)
end

我可以通过使用设置 reset_token 的工厂来绕过上述错误,但是用于路由的令牌与保存的摘要不匹配,测试的下一步失败。

到目前为止,我的结论是我误解了虚拟属性的处理方式,因为令牌是虚拟的,而摘要保存到数据库中。我怀疑我在规范中为我的路线获得的 reset_token 与生成摘要所涉及的不同,不知何故。

如果能帮助理解这里发生的事情,我们将不胜感激! :)

您收到路由错误,因为 reset_token 没有值;在这种情况下是您的 ID。

您可以在工厂创建的对象上设置/分配虚拟属性。只需将值传递给工厂即可:

let(:spec_user){ FactoryGirl.create :user,  reset_token: "some value"}

但是,对于您的用例,我猜您还需要将散列值存储在数据库中。 pasword_resets#edit 可能通过散列传入的 reset_token.

来查找用户记录

因此,在您的规范中:

require 'spec_helper'

describe "PasswordResets" do
  describe "#edit" do
    it "looks up the user and provides a form to reset the password" do
      # Generate the token in advance, so you can set the digest on the user
      reset_token = User.new_token
      # Create your user with the digest
      spec_user = FactoryGirl.create(:user, reset_token: reset_token, reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now)
      # Now visit path with an actual value for reset_tokeb
      visit(edit_password_reset_path(spec_user.reset_token, email: spec_user.email))
    end
  end
end