在Rspec中重用代码的最佳实践?

时间:2012-05-23 21:50:48

标签: ruby-on-rails ruby rspec capybara

我正在使用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并填写面向用户的表单要简单得多。

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

5 个答案:

答案 0 :(得分:9)

这里有几种不同的方式。首先,在这两种情况下,您都可以在describe或context块下对示例块进行分组,如下所示:

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 helper,我觉得它更具可读性。您可以查看有关它的更多信息here

答案 1 :(得分:7)

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

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

答案 2 :(得分:2)

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

迈克尔·哈特尔建议两种规格重复的解决方案:

  1. 定义常用操作的辅助方法(例如,登录用户)
  2. 定义自定义匹配器
  3. 使用这些东西有助于将测试与实现分离。

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

我会使用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