【问题标题】:Best practice for reusing code in Rspec?在 Rspec 中重用代码的最佳实践?
【发布时间】:2012-05-30 12:00:33
【问题描述】:

我正在使用 Rspec 和 Capybara 编写集成测试。我注意到在测试 activerecord 选项的创建时,我经常必须执行相同的代码。

例如:

it "should create a new instance" do
  # I create an instance here
end

it "should do something based on a new instance" do
  # I create an instance here
  # I click into the record and add a sub record, or something else
end

问题似乎是 ActiveRecord 对象不会在测试中保持不变,但是 Capybara 默认情况下会在规范中维护相同的会话(奇怪)。

我可以模拟这些记录,但由于这是一个集成测试,并且其中一些记录非常复杂(它们有图像附件等等),因此使用 Capybara 并填写面向用户的表单要简单得多。

我尝试定义一个创建新记录的函数,但由于某种原因感觉不对。这方面的最佳做法是什么?

【问题讨论】:

  • 请记住所有Examples(这是您的it 代码)都包含在事务中。我认为这对@Adam 回答很上瘾

标签: ruby-on-rails ruby rspec capybara


【解决方案1】:

这里有几种不同的方式。首先,在这两种情况下,您都可以将示例块分组到描述块或上下文块下,如下所示:

describe "your instance" do
  it "..." do
    # do stuff here
  end

  it "..." do
    # do other stuff here
  end
end

然后,在 describe 或 context 块中,您可以设置可以在所有示例中使用的状态,如下所示:

describe "your instance" do
  # run before each example block under the describe block
  before(:each) do
    # I create an instance here
  end

  it "creates a new instance" do
    # do stuff here
  end

  it "do something based on a new instance" do
    # do other stuff here
  end
end

作为 before(:each) 块的替代方法,您还可以使用 let 帮助器,我觉得它更具可读性。你可以看到更多关于它here

【讨论】:

    【解决方案2】:

    满足您要求的最佳实践是使用Factory Girl 从定义通用属性的蓝图创建记录,使用database_cleaner 跨不同测试/规范清理数据库。

    并且从不在不同的规范中保持状态(例如创建的记录),这将导致依赖规范。您可以使用 rspec 的 --order rand 选项发现这种依赖关系。如果您的规格随机失败,您就会遇到此类问题。

    【讨论】:

      【解决方案3】:

      鉴于标题(...在 Rspec 中重用代码),我建议阅读“Ruby on Rails 教程”中的RSpec custom matchers

      Michael Hartl 提出了两种解决规范重复的方法:

      1. 为常见操作定义辅助方法(例如登录用户)
      2. 定义自定义匹配器

      使用这些东西有助于将测试与实现分离。

      除了这些,我建议(如法比奥所说)使用 FactoryGirl。

      【讨论】:

        【解决方案4】:

        您可以查看我的示例 rails 项目。你可以在那里找到:https://github.com/lucassus/locomotive

        • 如何使用 factory_girl
        • 自定义匹配器和宏的一些示例(在 spec/support 中)
        • 如何使用shared_examples
        • 最后是如何使用非常好的 shoulda-macros

        【讨论】:

        • 您的示例 Rails 项目在哪里?
        【解决方案5】:

        我会结合使用 factory_girl 和 Rspec 的 let 方法:

        describe User do
          let(:user) { create :user } # 'create' is a factory_girl method, that will save a new user in the test database
        
          it "should be able to run" do
            user.run.should be_true
          end
        
          it "should not be able to walk" do
            user.walk.should be_false
          end
        end
        
        
        # spec/factories/users.rb
        FactoryGirl.define do
          factory :user do
            email { Faker::Internet.email }
            username { Faker::Internet.user_name }
          end
        end
        

        这使您可以像这样做很棒的事情:

        describe User do
          let(:user) { create :user, attributes }
          let(:attributes) { Hash.new }
        
          it "should be able to run" do
            user.run.should be_true
          end
        
          it "should not be able to walk" do
            user.walk.should be_false
          end
        
          context "when user is admin" do
            let(:attributes) { { admin: true } }
            it "should be able to walk" do
              user.walk.should be_true
            end
          end
        end
        

        【讨论】:

          猜你喜欢
          • 2015-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-20
          • 2018-08-05
          • 1970-01-01
          • 1970-01-01
          • 2012-01-13
          相关资源
          最近更新 更多