ruby RSpec 的主题和 let 有什么区别?什么时候应该使用它们?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38437162/
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
What's the difference between RSpec's subject and let? When should they be used or not?
提问by new2cpp
http://betterspecs.org/#subjecthas some info about subjectand let. However, I am still unclear on the difference between them. Furthermore, the SO post What is the argument against using before, let and subject in RSpec tests?said it is better to not use either subjector let. Where shall I go? I am so confused.
http://betterspecs.org/#subject有一些关于subject和 的信息let。但是,我仍然不清楚它们之间的区别。此外,SO 帖子反对在 RSpec 测试中使用 before、let 和 subject 的论点是什么?说最好不要使用subject或let。我要去哪里?我感到很困惑。
回答by Dave Schweisguth
Summary: RSpec's subject is a special variable that refers to the object being tested. Expectations can be set on it implicitly, which supports one-line examples. It is clear to the reader in some idiomatic cases, but is otherwise hard to understand and should be avoided. RSpec's letvariables are just lazily instantiated (memoized) variables. They aren't as hard to follow as the subject, but can still lead to tangled tests so should be used with discretion.
总结: RSpec 的主题是一个特殊的变量,它指的是被测试的对象。可以隐式设置期望值,支持单行示例。在一些惯用的情况下,读者很清楚,但在其他情况下很难理解,应该避免。RSpec 的let变量只是懒惰地实例化(记忆化)的变量。它们不像主题那样难以理解,但仍可能导致复杂的测试,因此应谨慎使用。
The subject
主题
How it works
这个怎么运作
The subject is the object being tested. RSpec has an explicit idea of the subject. It may or may not be defined. If it is, RSpec can call methods on it without referring to it explicitly.
主体是被测试的对象。RSpec 对这个主题有一个明确的想法。它可能会或可能不会被定义。如果是,RSpec 可以在不显式引用它的情况下调用它的方法。
By default, if the first argument to an outermost example group (describeor contextblock) is a class, RSpec creates an instance of that class and assigns it to the subject. For example, the following passes:
默认情况下,如果最外层示例组(describe或context块)的第一个参数是一个类,RSpec 创建该类的一个实例并将其分配给主题。例如,以下通过:
class A
end
describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end
You can define the subject yourself with subject:
您可以自己定义主题subject:
describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
You can give the subject a name when you define it:
您可以在定义主题时为其命名:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end
Even if you name the subject, you can still refer to it anonymously:
即使您命名了主题,您仍然可以匿名引用它:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
You can define more than one named subject. The most recently defined named subject is the anonymous subject.
您可以定义多个命名主题。最近定义的命名主题是匿名的subject。
However the subject is defined,
但是主题是定义的,
It's instantiated lazily. That is, the implicit instantiation of the described class or the execution of the block passed to
subjectdoesn't happen untilsubjector the named subject is referred to in an example. If you want your explict subject to be instantiated eagerly (before an example in its group runs), saysubject!instead ofsubject.Expectations can be set on it implicitly (without writing
subjector the name of a named subject):describe A do it { is_expected.to be_an(A) } endThe subject exists to support this one-line syntax.
它被懒惰地实例化。也就是说,所描述的类的隐式实例化或传递给的块的执行
subject不会发生,直到subject或在示例中引用了命名主体。如果您希望您的显式主题急切地实例化(在其组中的示例运行之前),请说subject!而不是subject。可以隐式设置期望值(无需书写
subject或命名主题的名称):describe A do it { is_expected.to be_an(A) } end该主题的存在是为了支持这种单行语法。
When to use it
何时使用
An implicit subject(inferred from the example group) is hard to understand because
隐式subject(从示例组推断)很难理解,因为
- It's instantiated behind the scenes.
- Whether it's used implicitly (by calling
is_expectedwithout an explicit receiver) or explicitly (assubject), it gives the reader no information about the role or nature of the object on which the expectation is being called. - The one-liner example syntax doesn't have an example description (the string argument to
itin the normal example syntax), so the only information the reader has about the purpose of the example is the expectation itself.
- 它在幕后实例化。
- 无论是隐式使用(通过在
is_expected没有显式接收器的情况下调用)还是显式使用(assubject),它都不会向读者提供有关调用期望的对象的角色或性质的信息。 - 单行示例语法没有示例描述(
it正常示例语法中的字符串参数),因此读者关于示例目的的唯一信息是期望本身。
Therefore, it's only helpful to use an implicit subject when the context is likely to be well understood by all readers and there is really no need for an example description. The canonical case is testing ActiveRecord validations with shoulda matchers:
因此,只有当上下文很可能被所有读者很好地理解并且确实不需要示例描述时,使用隐式主题才有帮助。典型案例是使用 shoulda 匹配器测试 ActiveRecord 验证:
describe Article do
it { is_expected.to validate_presence_of(:title) }
end
An explict anonymous subject(defined with subjectwithout a name) is a little better, because the reader can see how it's instantiated, but
显式匿名subject(定义为subject没有名称)会好一点,因为读者可以看到它是如何实例化的,但是
- it can still put the instantiation of the subject far from where it's used (e.g. at the top of an example group with many examples that use it), which is still hard to follow, and
- it has the other problems that the implicit subject does.
- 它仍然可以将主题的实例化放在远离它使用的地方(例如,在使用它的许多示例的示例组的顶部),这仍然很难理解,并且
- 它还有隐式主语会遇到的其他问题。
A named subject provides an intention-revealing name, but the only reason to use a named subject instead of a letvariable is if you want to use the anonymous subject some of the time, and we just explained why the anonymous subject is hard to understand.
命名主体提供了一个意图揭示名称,但使用命名主体而不是let变量的唯一原因是如果您有时想使用匿名主体,我们刚刚解释了为什么匿名主体难以理解。
So, legitimate uses of an explicit anonymous subjector a named subject are very rare.
因此,显式匿名subject或命名主体的合法使用非常罕见。
letvariables
let变量
How they work
它们是如何工作的
letvariables are just like named subjects except for two differences:
let除了两个不同之外,变量就像命名主题一样:
- they're defined with
let/let!instead ofsubject/subject! - they do not set the anonymous
subjector allow expectations to be called on it implicitly.
- 它们是用
let/let!而不是subject/定义的subject! - 他们不设置匿名
subject或允许隐式调用期望。
When to use them
何时使用它们
It's completely legitimate to use letto reduce duplication among examples. However, do so only when it doesn't sacrifice test clarity.The safest time to use letis when the letvariable's purpose is completely clear from its name (so that the reader doesn't have to find the definition, which could be many lines away, to understand each example) and it is used in the same way in every example. If either of those things isn't true, consider defining the object in a plain old local variable or calling a factory method right in the example.
let用于减少示例之间的重复是完全合法的。但是,只有在不牺牲测试清晰度的情况下才这样做。最安全的使用时间let是当let变量的用途从它的名称中完全清楚时(这样读者就不必找到定义,这可能是很多行之外,以理解每个示例)并且它以相同的方式使用在每个例子中。如果这些事情中的任何一个都不正确,请考虑在普通的旧局部变量中定义对象或在示例中直接调用工厂方法。
let!is risky, because it's not lazy.If someone adds an example to the example group that contains the let!, but the example doesn't need the let!variable,
let!是有风险的,因为它不懒惰。如果有人将示例添加到包含 的示例组let!,但示例不需要该let!变量,
- that example will be hard to understand, because the reader will see the
let!variable and wonder whether and how it affects the example - the example will be slower than it needs to be, because of the time taken to create the
let!variablle
- 该示例将难以理解,因为读者会看到
let!变量并想知道它是否以及如何影响示例 - 该示例将比它需要的慢,因为创建
let!变量所花费的时间
So use let!, if at all, only in small, simple example groups where it's less likely that future example writers will fall into that trap.
因此let!,如果有的话,请仅在小型、简单的示例组中使用,在这些组中,未来的示例编写者不太可能陷入该陷阱。
The single-expectation-per-example fetish
每个示例的单一期望恋物癖
There is a common overuse of subjects or letvariables that's worth discussing separately. Some people like to use them like this:
主题或let变量的常见过度使用值得单独讨论。有些人喜欢这样使用它们:
describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end
(This is a simple example of a method that returns a number for which we need two expectations, but this style can have many more examples/expectations if the method returns a more complicated value that needs many expectations and/or has many side effects that all need expectations.)
(这是一个简单的方法示例,它返回一个我们需要两个期望值的数字,但是如果该方法返回一个更复杂的值,需要很多期望值和/或有很多副作用,那么这种风格可以有更多的示例/期望值都需要期待。)
People do this because they've heard that one should have only one expectation per example (which is mixed up with the valid rule that one should only test one method call per example) or because they're in love with RSpec trickiness. Don't do it, whether with an anonymous or named subject or a letvariable! This style has several problems:
人们这样做是因为他们听说每个示例应该只有一个期望(这与每个示例只能测试一个方法调用的有效规则相混淆)或者因为他们喜欢 RSpec 的技巧。不要这样做,无论是匿名或命名主题还是let变量!这种风格有几个问题:
- The anonymous subject isn't the subject of the examples — the methodis the subject. Writing the test this way screws up the language, making it harder to think about.
- As always with one-line examples, there isn't any room to explain the meaning of the expectations.
- The subject has to be constructed for each example, which is slow.
- 匿名主题不是示例的主题——方法是主题。以这种方式编写测试搞砸了语言,使其更难思考。
- 与单行示例一样,没有任何空间来解释期望的含义。
- 必须为每个示例构建主题,这很慢。
Instead, write a single example:
相反,写一个例子:
describe 'Calculator' do
describe '#calculate' do
it "returns a single-digit number" do
result = Calculator.calculate
expect(result).to be >= 0
expect(result).to be <= 9
end
end
end
回答by Ren
Subjectand letare just tools to help you tidy up and speed up your tests. People in the rspec community do use them so i wouldn't worry about whether it's ok to use them or not. They can be used similarly but serve slightly different purposes
Subject并且let只是帮助您整理和加快测试的工具。rspec 社区中的人们确实使用它们,所以我不会担心是否可以使用它们。它们可以类似地使用,但用途略有不同
Subjectallows you to declare a test subject, and then reuse it for any number of following test cases afterward. This reduces code repetition (DRYing up your code)
Subject允许您声明一个测试主题,然后将其重用于任意数量的后续测试用例。这减少了代码重复(干掉你的代码)
Letis an alternative to before: eachblocks, which assign test data to instance variables. Letgives you a couple of advantages. First, it caches the value without assigning it to an instance variable. Second, it is lazily evaluated, which means that it doesn't get evaluated until a spec calls for it. Thus lethelps you speed up your tests. I also think letis easier to read
Let是before: each块的替代品,块将测试数据分配给实例变量。 Let给你几个优势。首先,它缓存该值而不将其分配给实例变量。其次,它是惰性求值的,这意味着在规范要求它之前不会对它求值。从而let帮助您加快测试速度。我也认为let更容易阅读
回答by nikkypx
subjectis what is under test, usually an instance or a class. letis for assigning variables in your tests, which are evaluated lazily vs. using instance variables. There are some nice examples in this thread.
subject是正在测试的内容,通常是一个实例或一个类。let用于在您的测试中分配变量,这些变量是惰性评估的,而不是使用实例变量。这个线程中有一些很好的例子。

