【问题标题】:How do I access a virtual attribute on a FactoryGirl-generated user in an Rspec/Capybara test?如何在 Rspec/Capybara 测试中访问 FactoryGirl 生成的用户的虚拟属性?
【发布时间】:2015-10-31 22:31:38
【问题描述】:

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

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

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

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

它给了我:

ActionController::UrlGenerationError: 没有路由匹配 {:action=>"edit", :controller=>"password_resets", :email=>"john2@example.com", :format=>nil, :id=>nil} 缺少必需的键:[: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 与生成摘要所涉及的不是同一个,不知何故。

任何帮助理解这里发生的事情将不胜感激! :)

【问题讨论】:

    标签: ruby-on-rails testing rspec factory-bot


    【解决方案1】:

    您收到路由错误,因为reset_token 没有值;在这种情况下你的身份证。

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

    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
    

    【讨论】:

    • 谢谢,但这不起作用。我根据您的建议进行了尝试,并直接在工厂中设置了所有属性,但在这两种情况下,我仍然没有得到编辑路径的 id。
    • 从头开始,这确实有效!不过,我不确定为什么。我打算放弃,然后我在工厂中注释掉了一个 transient 块,我在尝试解决这个问题时添加了这个块,它分配了(明文)令牌,突然这工作了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多