显式主题
在组范围内使用`subject` 来显式定义在示例范围内由`subject` 方法返回的值。
请注意,虽然下面的示例演示了如何使用`subject` 辅助方法作为用户面向的概念,但我们建议您将其保留用于支持自定义匹配器和/或扩展库,这些库将其实用隐藏在示例之外。
命名的`subject` 通过为其分配上下文语义名称来改进显式的`subject`。由于命名的`subject` 是显式的`subject`,因此它仍然定义了在示例范围内由`subject` 方法返回的值。但是,它定义了一个具有提供的名称的附加辅助方法。此辅助方法被记忆。该值在同一个示例中的多次调用中被缓存,但在示例之间不会被缓存。
我们建议在示例中使用命名的辅助方法而不是`subject`。
有关声明`subject` 的更多信息,请参阅API 文档。
可以在顶层组范围内定义和使用`subject`
给定一个名为“top_level_subject_spec.rb”的文件,其中包含
RSpec.describe Array, "with some elements" do
subject { [1, 2, 3] }
it "has the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
当我运行`rspec top_level_subject_spec.rb`
那么这些示例应该全部通过。
在外部组中定义的`subject` 可用于内部组
给定一个名为“nested_subject_spec.rb”的文件,其中包含
RSpec.describe Array do
subject { [1, 2, 3] }
describe "has some elements" do
it "which are the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
end
当我运行`rspec nested_subject_spec.rb`
那么这些示例应该全部通过。
`subject` 在示例中被记忆,但在示例之间不会被记忆
注意: 此场景显示了在`subject` 定义块中执行变异。此行为通常不鼓励,因为它使理解规范变得更加困难。此技术仅用作工具来演示记忆是如何发生的。
给定一个名为“memoized_subject_spec.rb”的文件,其中包含
RSpec.describe Array do
# This uses a context local variable. As you can see from the
# specs, it can mutate across examples. Use with caution.
element_list = [1, 2, 3]
subject { element_list.pop }
it "is memoized across calls (i.e. the block is invoked once)" do
expect {
3.times { subject }
}.to change{ element_list }.from([1, 2, 3]).to([1, 2])
expect(subject).to eq(3)
end
it "is not memoized across examples" do
expect{ subject }.to change{ element_list }.from([1, 2]).to([1])
expect(subject).to eq(2)
end
end
当我运行`rspec memoized_subject_spec.rb`
那么这些示例应该全部通过。
`subject` 在`before` 钩子中可用
给定一个名为“before_hook_subject_spec.rb”的文件,其中包含
RSpec.describe Array, "with some elements" do
subject { [] }
before { subject.push(1, 2, 3) }
it "has the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
当我运行`rspec before_hook_subject_spec.rb`
那么这些示例应该全部通过。
辅助方法可以从`subject` 定义块中调用
给定一个名为“helper_subject_spec.rb”的文件,其中包含
RSpec.describe Array, "with some elements" do
def prepared_array
[1, 2, 3]
end
subject { prepared_array }
it "has the prescribed elements" do
expect(subject).to eq([1, 2, 3])
end
end
当我运行`rspec helper_subject_spec.rb`
那么这些示例应该全部通过。
使用`subject!` 邦方法在示例之前调用定义块
给定一个名为“subject_bang_spec.rb”的文件,其中包含
RSpec.describe "eager loading with subject!" do
subject! { element_list.push(99) }
let(:element_list) { [1, 2, 3] }
it "calls the definition block before the example" do
element_list.push(5)
expect(element_list).to eq([1, 2, 3, 99, 5])
end
end
当我运行`rspec subject_bang_spec.rb`
那么这些示例应该全部通过。
使用`subject(:name)` 定义一个记忆的辅助方法
注意: 虽然下面的示例中使用了全局变量,但在实际规范中强烈不建议使用这种行为。这里只用它来演示该值将在同一个示例中的多次调用中被缓存,但在示例之间不会被缓存。
给定一个名为“named_subject_spec.rb”的文件,其中包含
$count = 0
RSpec.describe "named subject" do
subject(:global_count) { $count += 1 }
it "is memoized across calls (i.e. the block is invoked once)" do
expect {
2.times { global_count }
}.not_to change{ global_count }.from(1)
end
it "is not cached across examples" do
expect(global_count).to eq(2)
end
it "is still available using the subject method" do
expect(subject).to eq(3)
end
it "works with the one-liner syntax" do
is_expected.to eq(4)
end
it "the subject and named helpers return the same object" do
expect(global_count).to be(subject)
end
it "is set to the block return value (i.e. the global $count)" do
expect(global_count).to be($count)
end
end
当我运行`rspec named_subject_spec.rb`
那么这些示例应该全部通过。
使用`subject!(:name)` 定义一个在示例之前调用的名为`name` 的辅助方法
给定一个名为“named_subject_bang_spec.rb”的文件,其中包含
RSpec.describe "eager loading using a named subject!" do
subject!(:updated_list) { element_list.push(99) }
let(:element_list) { [1, 2, 3] }
it "calls the definition block before the example" do
element_list.push(5)
expect(element_list).to eq([1, 2, 3, 99, 5])
expect(updated_list).to be(element_list)
end
end
当我运行`rspec named_subject_bang_spec.rb`
那么这些示例应该全部通过。