Ruby-on-rails RSpec:let 和 before 块有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5974360/
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
RSpec: What is the difference between let and a before block?
提问by kriysna
What is difference between letand a beforeblock in RSpec?
RSpec 中let的before块和块有什么区别?
And when to use each?
什么时候使用?
What will be good approach (let or before) in below example?
在下面的例子中,什么是好的方法(让或之前)?
let(:user) { User.make !}
let(:account) {user.account.make!}
before(:each) do
@user = User.make!
@account = @user.account.make!
end
I studied this stackoverflow post
我研究了这个stackoverflow帖子
But is it good to define let for association stuff like above?
但是为上面的关联定义 let 是否好?
回答by Jay
People seem to have explained some of the basic ways in which they differ, but left out before(:all)and don't explain exactly why they should be used.
人们似乎已经解释了它们不同的一些基本方式,但被遗漏了before(:all)并且没有确切解释为什么应该使用它们。
It's my belief that instance variables have no place being used in the vast majority of specs, partly due to the reasons mentioned in this answer, so I won't mention them as an option here.
我相信实例变量在绝大多数规范中都没有使用的地方,部分原因是这个答案中提到的原因,所以我不会在这里提到它们作为一个选项。
let blocks
让块
Code within a letblock is only executed when referenced, lazy loading this means that ordering of these blocks is irrelevant. This gives you a large amount of power to cut down on repeated setup through your specs.
let块中的代码仅在被引用时执行,延迟加载这意味着这些块的顺序无关紧要。这为您提供了大量的能力来减少通过您的规格进行的重复设置。
One (extremely small and contrived) example of this is:
一个(非常小的和人为的)例子是:
let(:person) { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }
context 'with a moustache' do
let(:has_moustache) { true }
its(:awesome?) { should be_true }
end
context 'without a moustache' do
let(:has_moustache) { false }
its(:awesome?) { should be_false }
end
You can see that has_moustacheis defined differently in each case, but there's no need to repeat the subjectdefinition. Something important to note is that the last letblock defined in the current context will be used. This is good for setting a default to be used for the majority of specs, which can be overwritten if needed.
您可以看到has_moustache在每种情况下都有不同的定义,但无需重复subject定义。需要注意的重要一点是let将使用当前上下文中定义的最后一个块。这对于设置用于大多数规范的默认值很有用,如果需要,可以覆盖这些规范。
For instance, checking the return value of calculate_awesomeif passed a personmodel with top_hatset to true, but no moustache would be:
例如,检查calculate_awesomeif 传递的person模型的返回值top_hat设置为 true,但没有胡子将是:
context 'without a moustache but with a top hat' do
let(:has_moustache) { false }
let(:person) { build(:person, top_hat: true) }
its(:awesome?) { should be_true }
end
Another thing to note about let blocks, they should not be used if you're searching for something which has been saved to the database (i.e. Library.find_awesome_people(search_criteria)) as they will not be saved to the database unless they have already been referenced. let!or beforeblocks are what should be used here.
关于 let 块要注意的另一件事是,如果您正在搜索已保存到数据库中的内容(即Library.find_awesome_people(search_criteria)),则不应使用它们,因为除非它们已被引用,否则它们不会保存到数据库中。let!或before块是这里应该使用的。
Also, never everuse beforeto trigger execution of letblocks, this is what let!is made for!
此外,从来没有过使用before到的触发器执行let块,这是let!用于制造!
let! blocks
让!块
let!blocks are executed in the order they are defined (much like a before block). The one core difference to before blocks is that you get an explicit reference to this variable, rather than needing to fall back to instance variables.
let!块按照它们定义的顺序执行(很像之前的块)。与 before 块的一个核心区别是您获得了对该变量的显式引用,而不是需要回退到实例变量。
As with letblocks, if multiple let!blocks are defined with the same name, the most recent is what will be used in execution. The core difference being that let!blocks will be executed multiple times if used like this, whereas the letblock will only execute the last time.
与let块一样,如果let!定义了多个具有相同名称的块,则将在执行中使用最新的块。核心区别在于,let!如果像这样使用,块将被执行多次,而let块只会执行最后一次。
before(:each) blocks
before(:each) 块
before(:each)is the default before block, and can therefore be referenced as before {}rather than specifying the full before(:each) {}each time.
before(:each)是块之前的默认值,因此可以作为before {}而不是before(:each) {}每次都指定完整来引用。
It's my personal preference to use beforeblocks in a few core situations. I will use before blocks if:
before在一些核心情况下使用块是我个人的偏好。如果出现以下情况,我将使用 before 块:
- I'm using mocking, stubbing or doubles
- There is any reasonable sized setup (generally this is a sign your factory traits haven't been setup correctly)
- There's a number of variables which I don't need to reference directly, but are required for setup
- I'm writing functional controller tests in rails, and I want to execute a specific request to test (i.e.
before { get :index }). Even though you could usesubjectfor this in a lot of cases, it sometimes feels more explicit if you don't require a reference.
- 我正在使用嘲笑、存根或双打
- 有任何合理大小的设置(通常这表明您的工厂特征尚未正确设置)
- 有许多变量我不需要直接引用,但是设置需要
- 我正在 Rails 中编写功能控制器测试,并且我想执行一个特定的测试请求(即
before { get :index })。尽管您可以subject在很多情况下使用它,但如果您不需要参考,有时会感觉更明确。
If you find yourself writing large beforeblocks for your specs, check your factories and make sure you fully understand traits and their flexibility.
如果您发现自己before为规范编写了大块,请检查您的工厂并确保您完全了解特征及其灵活性。
before(:all) blocks
before(:all) 块
These are only ever executed once, before the specs in the current context (and its children). These can be used to great advantage if written correctly, as there are certain situations this can cut down on execution and effort.
这些仅在当前上下文(及其子项)中的规范之前执行一次。如果编写正确,这些可以发挥很大的优势,因为在某些情况下,这可以减少执行和工作量。
One example (which would hardly affect execution time at all) is mocking out an ENV variable for a test, which you should only ever need to do once.
一个例子(它几乎不会影响执行时间)是模拟一个测试的 ENV 变量,你应该只需要做一次。
Hope that helps :)
希望有帮助:)
回答by Spyros
Almost always, I prefer let. The post that you link specifies that letis also faster. However, at times, when many commands have to be executed, I could use before(:each)because its syntax is more clear when many commands are involved.
几乎总是,我更喜欢let. 您链接的帖子指定let也更快。但是,有时,当必须执行许多命令时,我可以使用before(:each)它,因为当涉及许多命令时,它的语法更加清晰。
In your example, I would definitely prefer to use letinstead of before(:each). Generally speaking, when just some variable initialization is done, I tend to like using let.
在您的示例中,我肯定更喜欢使用let而不是before(:each). 一般来说,当一些变量初始化完成时,我倾向于使用let.
回答by mikeweber
A big difference that hasn't been mentioned is that variables defined with letdon't get instantiated until you call it the first time. So while a before(:each)block would instantiate all of the variables, letlet's you define a number of variables you might use across multiple tests, it doesn't automatically instantiate them. Without knowing this, your tests could come back to bite yourself if you're expecting all of the data to be loaded beforehand. In some cases, you may even want to define a number of letvariables, then use a before(:each)block to call each letinstance just to make sure the data is available to begin with.
没有提到的一个很大的区别是定义的变量let在你第一次调用它之前不会被实例化。因此,虽然before(:each)块会实例化所有变量,但let让我们定义一些可能在多个测试中使用的变量,它不会自动实例化它们。在不知道这一点的情况下,如果您希望预先加载所有数据,那么您的测试可能会回来咬自己。在某些情况下,您甚至可能想要定义多个let变量,然后使用before(:each)块来调用每个let实例,以确保开始时数据可用。
回答by Tim Connor
It looks like you are using Machinist. Beware, you may see some issues with make! inside of let (the non-bang version) happening outside of the global fixture transaction (if you are using transactional fixtures as well) so corrupting the data for your other tests.
看起来您正在使用机械师。请注意,您可能会看到 make 的一些问题!let 内部(非 bang 版本)发生在全局夹具事务之外(如果您也使用事务夹具),因此会破坏其他测试的数据。

