File: explicit_subject.feature

package info (click to toggle)
ruby-rspec 3.13.0c0e0m0s1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,856 kB
  • sloc: ruby: 70,868; sh: 1,423; makefile: 99
file content (187 lines) | stat: -rw-r--r-- 6,707 bytes parent folder | download | duplicates (6)
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