1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
Feature: Explicit Subject
Use `subject` in the group scope to explicitly define the value that is returned by the
`subject` method in the example scope.
Note that while the examples below demonstrate how the `subject` helper can be used
as a user-facing concept, we recommend that you reserve it for support of custom
matchers and/or extension libraries that hide its use from examples.
A named `subject` improves on the explicit `subject` by assigning it a contextually
semantic name. Since a named `subject` is an explicit `subject`, it still defines the value
that is returned by the `subject` method in the example scope. However, it defines an
additional helper method with the provided name. This helper method is memoized.
The value is cached across multiple calls in the same example but not across examples.
We recommend using the named helper method over `subject` in examples.
For more information about declaring a `subject` see the [API docs](http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/MemoizedHelpers/ClassMethods#subject-instance_method).
Scenario: A `subject` can be defined and used in the top level group scope
Given a file named "top_level_subject_spec.rb" with:
"""ruby
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
"""
When I run `rspec top_level_subject_spec.rb`
Then the examples should all pass
Scenario: The `subject` defined in an outer group is available to inner groups
Given a file named "nested_subject_spec.rb" with:
"""ruby
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
"""
When I run `rspec nested_subject_spec.rb`
Then the examples should all pass
Scenario: The `subject` is memoized within an example but not across examples
**Note:** This scenario shows mutation being performed in a `subject` definition block. This
behavior is generally discouraged as it makes it more difficult to understand the specs.
This is technique is used simply as a tool to demonstrate how the memoization occurs.
Given a file named "memoized_subject_spec.rb" with:
"""ruby
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
"""
When I run `rspec memoized_subject_spec.rb`
Then the examples should all pass
Scenario: The `subject` is available in `before` hooks
Given a file named "before_hook_subject_spec.rb" with:
"""ruby
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
"""
When I run `rspec before_hook_subject_spec.rb`
Then the examples should all pass
Scenario: Helper methods can be invoked from a `subject` definition block
Given a file named "helper_subject_spec.rb" with:
"""ruby
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
"""
When I run `rspec helper_subject_spec.rb`
Then the examples should all pass
Scenario: Use the `subject!` bang method to call the definition block before the example
Given a file named "subject_bang_spec.rb" with:
"""ruby
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
"""
When I run `rspec subject_bang_spec.rb`
Then the examples should all pass
Scenario: Use `subject(:name)` to define a memoized helper method
**Note:** While a global variable is used in the examples below, this behavior is strongly
discouraged in actual specs. It is used here simply to demonstrate the value will be
cached across multiple calls in the same example but not across examples.
Given a file named "named_subject_spec.rb" with:
"""ruby
$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
"""
When I run `rspec named_subject_spec.rb`
Then the examples should all pass
Scenario: Use `subject!(:name)` to define a helper method called before the example
Given a file named "named_subject_bang_spec.rb" with:
"""ruby
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
"""
When I run `rspec named_subject_bang_spec.rb`
Then the examples should all pass
|