如何声明RSpec中的示例之间共享的变量?

时间:2014-12-28 18:42:42

标签: ruby variables rspec

假设我有以下规范:

...
describe Thing do

  it 'can read data' do
     @data = get_data_from_file  # [ '42', '36' ]
     expect(@data.count).to eq 2
  end

  it 'can process data' do
     expect(@data[0].to_i).to eq 42  # Fails because @data is nil
  end

end
...

我想要的是在给定的 describe context 中共享一个变量。我会在一个例子中写一个值,然后在另一个例子中读取它。我该怎么做?

3 个答案:

答案 0 :(得分:44)

您应该使用before(:each)before(:all)阻止:

describe Thing do
  before(:each) do
    @data = get_data_from_file  # [ '42', '36' ]
  end

  it 'can read data' do
    expect(@data.count).to eq 2
  end

  it 'can process data' do
    expect(@data[0].to_i).to eq 42
  end
end

不同之处在于before(:each)将分别针对每个案例执行before(:all),并describe/context在此before(:each)中的所有示例之前执行一次before(:all)。我建议您更喜欢before(:all)而不是get_data_from_file,因为在这种情况下每个示例都会被隔离,这是一个很好的做法。

在极少数情况下,如果您想使用before(:all),例如@data执行时间较长,在这种情况下,您可以牺牲测试隔离来支持速度。但我想知道您,在使用it时,在一次测试(describe/context阻止)中修改before(:all)变量会对describe MyClass do before(:all) do @a = [] end it { @a << 1; p @a } it { @a << 2; p @a } it { @a << 3; p @a } end 中的其他测试产生意外后果范围,因为他们将分享它。

[1] [1, 2] [1, 2, 3] 示例:

describe MyClass do
  before(:all) do
    @a = []
  end

  it { @a = [1]; p @a }
  it { p @a }
end

将输出:

[1]
[]

<强>已更新

回答你的问题

it

将输出

before(:all)

因为在第一个it中你在本地分配实例变量@a,所以它与object_id块中的@a不同,并且对其他it块不可见,您可以输出{{1}}来检查它。因此,只有修改才能完成,分配将导致新的对象创建。

因此,如果您多次分配变量,那么最终可能会有一个{{1}}块和多个期望值。 It is acceptable, according to best practices.

答案 1 :(得分:10)

这就是RSpec let helper的目的,它允许您使用代码执行此操作:

...
describe Thing do
  let(:data) { get_data_from_file }

  it 'can read data' do
     expect(data.count).to eq 2
  end

  it 'can process data' do
     expect(data[0].to_i).to eq 42
  end

end
...

答案 2 :(得分:0)

我刚遇到同样的问题。我如何解决它是通过使用factory_girl gem。

以下是基础知识:

创建工厂(这是代码片段:

require 'factory_girl'
require 'faker' # you can use faker, if you want to use the factory to generate fake data

FactoryGirl.define do
  factory :generate_data, class: MyModule::MyClass do
    key 'value'
  end
end

现在你出厂后需要制作一个如下所示的模型:

Module MyModule
  class MyClass
    attr_accessor :key

    #you can also place methods here to call from your spec test, if you wish
    # def self.test
        #some test
    # end
  end
end

现在回到你的例子,你可以这样做:

describe Thing do
  before(:all) do
  @data = FactoryGirl.build(:generate_data)
  end

  it 'can read data' do
     @data.key = get_data_from_file  # [ '42', '36' ]
     expect(@data.key.count).to eq 2
  end

  it 'can process data' do
     expect(@data.key[0].to_i).to eq 42  # @data will not be nil. at this point. whatever @data.key is equal to last which was set in your previous context will be what data.key is here
  end

end

无论如何,祝你好运,如果你有其他解决方案,我们就知道了!