File: matching_arguments.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 (216 lines) | stat: -rw-r--r-- 10,926 bytes parent folder | download
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
Feature: Matching arguments

  Use `with` to specify the expected arguments. A [message expectation](../basics/expecting-messages) constrained by `with`
  will only be satisfied when called with matching arguments. A canned response for an
  [allowed message](../basics/allowing-messages) will only be used when the arguments match.

  | To match...                                         | ...use an expression like:         | ...which matches calls like:                        |
  |-----------------------------------------------------|------------------------------------|-----------------------------------------------------|
  | Literal arguments                                   | `with(1, true)`                    | `foo(1, true)`                                      |
  | Literal arguments where one is a hash               | `with(1, {x: 1, y: 2})`            | `foo(1, x: 1, y: 2) (where last argument is a hash) |
  | Keyword arguments                                   | `with(x: 1, y: 2)`                 | `foo(x: 1, y: 2)` (where x and y are keywords)      |
  | Anything that supports case equality (`===`)        | `with(/bar/)`                      | `foo("barn")`                                       |
  | Any list of args                                    | `with(any_args)`                   | `foo()`<br>`foo(1)`<br>`foo(:bar, 2)`               |
  | Any sublist of args (like an arg splat)             | `with(1, any_args)`                | `foo(1)`<br>`foo(1, :bar, :bazz)`                   |
  | An empty list of args                               | `with(no_args)`                    | `foo()`                                             |
  | Anything for a given positional arg                 | `with(3, anything)`                | `foo(3, nil)`<br>`foo(3, :bar)`                     |
  | Against an interface                                | `with(duck_type(:each))`           | `foo([])`                                           |
  | A boolean                                           | `with(3, boolean)`                 | `foo(3, true)`<br>`foo(3, false)`                   |
  | A subset of a hash                                  | `with(hash_including(:a => 1))`    | `foo(:a => 1, :b => 2)`                             |
  | An excluded subset of a hash                        | `with(hash_excluding(:a => 1))`    | `foo(:b => 2)`                                      |
  | A subset of an array                                | `with(array_including(:a, :b))`    | `foo([:a, :b, :c])`                                 |
  | An excluded subset of an array                      | `with(array_excluding(:a, :b))`    | `foo([:c, :d])`                                     |
  | An instance of a specific class                     | `with(instance_of(Integer))`       | `foo(3)`                                            |
  | An object with a given module in its ancestors list | `with(kind_of(Numeric))`           | `foo(3)`                                            |
  | An object with matching attributes                  | `with(having_attributes(:a => 1))` | `foo(:a => 1, :b => 2)`                             |
  | Any RSpec matcher                                   | `with(<matcher>)`                  | `foo(<object that matches>)`                        |

  Scenario: Basic example
    Given a file named "basic_example_spec.rb" with:
      """ruby
      RSpec.describe "Constraining a message expectation using with" do
        let(:dbl) { double }
        before { expect(dbl).to receive(:foo).with(1, anything, /bar/) }

        it "passes when the args match" do
          dbl.foo(1, nil, "barn")
        end

        it "fails when the args do not match" do
          dbl.foo(1, nil, "other")
        end
      end
      """
    When I run `rspec basic_example_spec.rb`
    Then it should fail with the following output:
      | 2 examples, 1 failure                                           |
      |                                                                 |
      | Failure/Error: dbl.foo(1, nil, "other")                         |
      |   #<Double (anonymous)> received :foo with unexpected arguments |
      |     expected: (1, anything, /bar/)                              |
      |          got: (1, nil, "other")                                 |

  @kw-arguments
  Scenario: Using keyword arguments
    Given a file named "keyword_example_spec.rb" with:
      """ruby
      class WithKeywords
        def foo(bar: "")
        end
      end

      RSpec.describe "Constraining a message expectation using with" do
        let(:dbl) { instance_double(WithKeywords) }
        before { expect(dbl).to receive(:foo).with(bar: "baz") }

        it "passes when the args match" do
          dbl.foo(bar: "baz")
        end

        it "fails when the args do not match" do
          dbl.foo(bar: "incorrect")
        end
      end
      """
    When I run `rspec keyword_example_spec.rb`
    Then it should fail with the following output:
      | 2 examples, 1 failure                                                                 |
      |                                                                                       |
      | Failure/Error: dbl.foo(bar: "incorrect")                                              |
      |   #<InstanceDouble(WithKeywords) (anonymous)> received :foo with unexpected arguments |
      |     expected: ({:bar=>"baz"})                                                         |
      |          got: ({:bar=>"incorrect"})                                                   |

  @distincts_kw_args_from_positional_hash
  Scenario: Using keyword arguments on Rubies that differentiate hashes from keyword arguments
    Given a file named "keyword_example_spec.rb" with:
      """ruby
      class WithKeywords
        def foo(bar: "")
        end
      end

      RSpec.describe "Constraining a message expectation using with" do
        let(:dbl) { instance_double(WithKeywords) }
        before { expect(dbl).to receive(:foo).with(bar: "baz") }

        it "fails when the args do not match due to a hash" do
          dbl.foo({bar: "also incorrect"})
        end
      end
      """
    When I run `rspec keyword_example_spec.rb`
    Then it should fail with the following output:
      | 1 example, 1 failure                                                                  |
      |                                                                                       |
      | Failure/Error: dbl.foo({bar: "also incorrect"})                                       |
      |   #<InstanceDouble(WithKeywords) (anonymous)> received :foo with unexpected arguments |
      |     expected: ({:bar=>"baz"}) (keyword arguments)                                     |
      |          got: ({:bar=>"also incorrect"}) (options hash)                               |

  Scenario: Using a RSpec matcher
    Given a file named "rspec_matcher_spec.rb" with:
      """ruby
      RSpec.describe "Using a RSpec matcher" do
        let(:dbl) { double }
        before { expect(dbl).to receive(:foo).with(a_collection_containing_exactly(1, 2)) }

        it "passes when the args match" do
          dbl.foo([2, 1])
        end

        it "fails when the args do not match" do
          dbl.foo([1, 3])
        end
      end
      """
    When I run `rspec rspec_matcher_spec.rb`
    Then it should fail with the following output:
      | 2 examples, 1 failure                                         |
      |                                                               |
      | Failure/Error: dbl.foo([1, 3])                                |
      | #<Double (anonymous)> received :foo with unexpected arguments |
      | expected: (a collection containing exactly 1 and 2)           |
      | got: ([1, 3])                                                 |

  @ripper
  Scenario: Using satisfy for complex custom expecations
    Given a file named "rspec_satisfy_spec.rb" with:
      """ruby
      RSpec.describe "Using satisfy for complex custom expecations" do
        let(:dbl) { double }

        def a_b_c_equals_5
          satisfy { |data| data[:a][:b][:c] == 5 }
        end

        it "passes when the expectation is true" do
          expect(dbl).to receive(:foo).with(a_b_c_equals_5)
          dbl.foo({ :a => { :b => { :c => 5 } } })
        end

        it "fails when the expectation is false" do
          expect(dbl).to receive(:foo).with(a_b_c_equals_5)
          dbl.foo({ :a => { :b => { :c => 3 } } })
        end
      end
      """
    When I run `rspec rspec_satisfy_spec.rb`
    Then it should fail with the following output:
      | 2 examples, 1 failure                                         |
      |                                                               |
      | Failure/Error: dbl.foo({ :a => { :b => { :c => 3 } } })       |
      | #<Double (anonymous)> received :foo with unexpected arguments |
      | expected: (satisfy expression `data[:a][:b][:c] == 5`)        |
      |      got: ({:a=>{:b=>{:c=>3}}})                               |

  Scenario: Using a custom matcher
    Given a file named "custom_matcher_spec.rb" with:
      """ruby
      RSpec::Matchers.define :a_multiple_of do |x|
        match { |actual| (actual % x).zero? }
      end

      RSpec.describe "Using a custom matcher" do
        let(:dbl) { double }
        before { expect(dbl).to receive(:foo).with(a_multiple_of(3)) }

        it "passes when the args match" do
          dbl.foo(12)
        end

        it "fails when the args do not match" do
          dbl.foo(13)
        end
      end
      """
    When I run `rspec custom_matcher_spec.rb`
    Then it should fail with the following output:
      | 2 examples, 1 failure                                           |
      |                                                                 |
      | Failure/Error: dbl.foo(13)                                      |
      |   #<Double (anonymous)> received :foo with unexpected arguments |
      |     expected: (a multiple of 3)                                 |
      |          got: (13)                                              |

  Scenario: Responding differently based on the arguments
    Given a file named "responding_differently_spec.rb" with:
      """ruby
      RSpec.describe "Using #with to constrain responses" do
        specify "its response depends on the arguments" do
          dbl = double

          # Set a default for any unmatched args
          allow(dbl).to receive(:foo).and_return(:default)

          allow(dbl).to receive(:foo).with(1).and_return(1)
          allow(dbl).to receive(:foo).with(2).and_return(2)

          expect(dbl.foo(0)).to eq(:default)
          expect(dbl.foo(1)).to eq(1)
          expect(dbl.foo(2)).to eq(2)
        end
      end
      """
    When I run `rspec responding_differently_spec.rb`
    Then the examples should all pass