如何声明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 ... 

我想要的只是在给定的描述上下文中共享一个变量。 我会在一个例子中写一个值,然后在另一个例子中读取它。 我怎么做?

您应该使用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)分别执行before(:each)在此describe/context所有示例before(:all)一次。 我建议你before(:each) before(:all)更喜欢before(:all) ,因为在这种情况下每个例子都是孤立的,这是一个很好的做法。

在极少数情况下,如果你想使用before(:all) ,例如你的get_data_from_file执行时间很长,那么你当然可以牺牲测试隔离来支持速度。 但我想知道你,在使用before(:all) ,在一个测试( it阻塞)中修改你的@data变量会导致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] [] 

因为首先你在本地分配实例变量@a,所以它与before(:all)块中的@a不同,并且对其他块不可见,你可以通过输出object_id来检查它。 因此,只有修改才能完成,分配将导致新的对象创建。

因此,如果您多次分配变量,您应该最终得到一个阻塞和多个期望。 根据最佳实践,这是可以接受的。

这实际上是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 ... 

我刚遇到同样的问题。 我如何解决它是通过使用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 

无论如何,祝你好运,如果你有其他解决方案!