ruby 如何声明在 RSpec 中的示例之间共享的变量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27679456/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to declare a variable shared between examples in RSpec?
提问by Andrey Esperanza
Suppose I have the following spec:
假设我有以下规格:
...
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
...
All I wanted is to have a variable shared in the given describeor context. I would write a value in one example and read it in another. How do I do that?
我想要的只是在给定的describe或context 中共享一个变量。我会在一个示例中写入一个值并在另一个示例中读取它。我怎么做?
回答by Rustam A. Gasanov
You should use before(:each)or before(:all)block:
您应该使用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
The difference is that before(:each)will be executed for each case separately and before(:all)once before all examples in this describe/context. I would recommend you to prefer before(:each)over before(:all), because each example will be isolated in this case which is a good practice.
不同之处在于,before(:each)将针对每个案例单独执行before(:all)一次,并且在此中的所有示例之前执行一次describe/context。我建议您选择before(:each)over before(:all),因为在这种情况下每个示例都将被隔离,这是一个很好的做法。
There are rare cases when you want to use before(:all), for example if your get_data_from_filehas a long execution time, in this case you can, of course, sacrifice tests isolation in favor of speed. But I want to aware you, that when using before(:all), modification of your @datavariable in one test(itblock) will lead to unexpected consequences for other tests in describe/contextscope because they will share it.
在极少数情况下,您想使用before(:all),例如,如果您get_data_from_file的执行时间很长,在这种情况下,您当然可以牺牲测试隔离来提高速度。但是我想提醒您,在一个 test(块) 中使用before(:all), 修改您的@data变量it将导致describe/context范围内的其他测试出现意外后果,因为它们会共享它。
before(:all)example:
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
Will output:
将输出:
[1]
[1, 2]
[1, 2, 3]
UPDATED
更新
To answer you question
回答你的问题
describe MyClass do
before(:all) do
@a = []
end
it { @a = [1]; p @a }
it { p @a }
end
Will output
会输出
[1]
[]
Because in first ityou are locally assigning instance variable @a, so it isn't same with @a in before(:all)block and isn't visible to other itblocks, you can check it, by outputting object_ids. So only modification will do the trick, assignment will cause new object creation.
因为首先it您是在本地分配实例变量@a,所以它与before(:all)块中的@a 不同,并且对其他it块不可见,您可以通过输出object_ids来检查它。所以只有修改才能奏效,赋值会导致新对象的创建。
So if you are assigning variable multiple times you should probably end up with one itblock and multiple expectation in it. It is acceptable, according to best practices.
因此,如果您多次分配变量,您可能最终会得到一个it块和多个期望。根据最佳实践,这是可以接受的。
回答by Anthony
This is really the purpose of the RSpec let helperwhich allows you to do this with your code:
这确实是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
...
回答by adbarads
I just ran into this same problem. How I solved it was by using factory_girl gem.
我刚刚遇到了同样的问题。我是通过使用 factory_girl gem 来解决它的。
Here's the basics:
以下是基础知识:
create a factory (here's a code snippet:
创建一个工厂(这是一个代码片段:
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
Now after you made the factory you need to make a Model that looks like this:
现在,在您制作工厂之后,您需要制作一个如下所示的模型:
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
Now going back to your example you can do something like this:
现在回到你的例子,你可以做这样的事情:
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
Anyways, good luck let us know, if you got some other solution!
无论如何,如果您有其他解决方案,请告诉我们,祝您好运!

