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

And*_*nza 36 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
...
Run Code Online (Sandbox Code Playgroud)

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

Rus*_*nov 46

你应该使用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
Run Code Online (Sandbox Code Playgroud)

区别在于,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
Run Code Online (Sandbox Code Playgroud)

将输出:

[1]
[1, 2]
[1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

更新

回答你的问题

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

  it { @a = [1]; p @a }
  it { p @a }
end
Run Code Online (Sandbox Code Playgroud)

会输出

[1]
[]
Run Code Online (Sandbox Code Playgroud)

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

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


Ant*_*ony 13

这实际上是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
...
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的回答,但正如您从上面的问题中看到的,我们的想法是在一个示例中设置一个变量并在另一个示例中读取/更新它,在同一个“描述”或“上下文”块中。 (2认同)