http://betterspecs.org/#subject有一些关于subject
和let
的信息.然而,我仍然不清楚它们之间的区别.此外,SO post What is the argument against using before, let and subject in RSpec tests?表示最好不要使用subject
或let
.我该go 哪里?我很困惑.
http://betterspecs.org/#subject有一些关于subject
和let
的信息.然而,我仍然不清楚它们之间的区别.此外,SO post What is the argument against using before, let and subject in RSpec tests?表示最好不要使用subject
或let
.我该go 哪里?我很困惑.
总结:RSpec的主题是一个特殊变量,指的是被测试的对象.期望值可以隐式设置,这支持一行示例.在一些惯用的情况下,读者很清楚这一点,但在其他方面很难理解,应该避免.RSpec的let
个变量只是延迟实例化(记忆化)的变量.它们不像主题那么难理解,但仍然会导致复杂的测试,因此应该谨慎使用.
受试者就是被测试的对象.RSpec对这个主题有明确的概念.它可以定义,也可以不定义.如果是,RSpec可以对其调用方法,而无需显式引用它.
默认情况下,如果最外层示例组(describe
或context
块)的第一个参数是一个类,RSpec将创建该类的实例并将其分配给主题.例如,以下过程:
class A
end
describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end
你可以用subject
来定义主题:
describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
您可以在定义主题时为其命名:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end
即使你命名了主题,你仍然可以匿名引用它:
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
可以定义多个命名主题.最近定义的命名主题是匿名subject
.
无论主题如何定义,
它被惰性地实例化.也就是说,所描述的类的隐式实例化或传递给subject
的块的执行直到subject
或在示例中引用指定的主题时才会发生.如果你想让你的解释主题被Eager 地实例化(在其组中的一个例子运行之前),可以说是subject!
而不是subject
.
可以隐式地设置期望值(无需写subject
或指定主题的名称):
describe A do
it { is_expected.to be_an(A) }
end
主题的存在是为了支持这一行语法.
隐式subject
(从示例组推断)很难理解,因为
is_expected
而不使用显式接收器)还是显式使用(如subject
),它都不会向读者提供有关调用期望的对象的角色或性质的信息.it
),因此读者关于示例目的的唯一信息是期望本身.因此,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.规范 case 是使用shoulda matchers测试ActiveRecord验证:
describe Article do
it { is_expected.to validate_presence_of(:title) }
end
explict anonymous subject
(定义为没有名字的subject
)更好一些,因为读者可以看到它是如何实例化的,但是
命名主题提供了一个意图揭示名称,但使用命名主题而不是let
变量的唯一原因是,如果你想在某些时候使用匿名主题,我们刚才解释了为什么匿名主题很难理解.
所以,legitimate uses of an explicit anonymous 100 or a named subject are very rare.
let
variableslet
个变量与命名主题相似,只是有两个不同:
let
/let!
,而不是subject
/subject!
subject
,也不允许隐式调用期望值.It's completely legitimate to use 100 to reduce duplication among examples. However, do so only when it doesn't sacrifice test clarity.使用let
最安全的时间是当let
变量的用途从其名称中完全清楚时(这样读者就不必找到定义,可能有很多行之遥,才能理解每个示例),并且在每个示例中都以相同的方式使用它.如果其中任何一个都不是真的,请考虑在一个简单的老局部变量中定义对象,或者在该示例中调用一个工厂方法.
100 is risky, because it's not lazy.如果有人向包含let!
的示例组添加了一个示例,但该示例不需要let!
变量,
let!
变量,并想知道它是否以及如何影响这个例子let!
变量所需的时间,该示例将比需要的慢所以,如果有的话,只在小的、简单的示例组中使用let!
,这样future 的示例作者就不太可能落入这个trap .
有一个共同的过度使用的主题或let
个变量,值得单独讨论.有些人喜欢这样使用它们:
describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end
(这是一个简单的方法示例,该方法返回的数字需要两个期望值,但如果该方法返回的值更复杂,需要很多期望值,并且/或者有很多副作用,这些副作用都需要期望值,那么这种风格可以有更多的示例/期望值.)
人们这样做是因为他们听说每个示例应该只有一个期望值(这与每个示例只能测试一个方法调用的有效规则相混淆),或者因为他们喜欢RSpec的诡计.不要这样做,无论是匿名主题、命名主题还是let
变量!这种风格有几个问题:
相反,写一个例子:
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