Method: Minitest::Mock#method_missing

Defined in:
lib/minitest/mock.rb

#method_missing(sym, *args, **kwargs, &block) ⇒ Object

:nodoc:


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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/minitest/mock.rb', line 152

def method_missing sym, *args, **kwargs, &block # :nodoc:
  unless @expected_calls.key? sym then
    if @delegator && @delegator.respond_to?(sym)
      if kwargs.empty? then # FIX: drop this after 2.7 dead
        return @delegator.public_send(sym, *args, &block)
      else
        return @delegator.public_send(sym, *args, **kwargs, &block)
      end
    else
      raise NoMethodError, "unmocked method %p, expected one of %p" %
        [sym, @expected_calls.keys.sort_by(&:to_s)]
    end
  end

  index = @actual_calls[sym].length
  expected_call = @expected_calls[sym][index]

  unless expected_call then
    raise MockExpectationError, "No more expects available for %p: %p %p" %
      [sym, args, kwargs]
  end

  expected_args, expected_kwargs, retval, val_block =
    expected_call.values_at :args, :kwargs, :retval, :block

  expected_kwargs = kwargs.to_h { |ak, av| [ak, Object] } if
    Hash == expected_kwargs

  if val_block then
    # keep "verify" happy
    @actual_calls[sym] << expected_call

    raise MockExpectationError, "mocked method %p failed block w/ %p %p" %
      [sym, args, kwargs] unless val_block.call(*args, **kwargs, &block)

    return retval
  end

  if expected_args.size != args.size then
    raise ArgumentError, "mocked method %p expects %d arguments, got %p" %
      [sym, expected_args.size, args]
  end

  if expected_kwargs.size != kwargs.size then
    raise ArgumentError, "mocked method %p expects %d keyword arguments, got %p" %
      [sym, expected_kwargs.size, kwargs]
  end

  zipped_args = expected_args.zip args
  fully_matched = zipped_args.all? { |mod, a|
    mod === a or mod == a
  }

  unless fully_matched then
    fmt = "mocked method %p called with unexpected arguments %p"
    raise MockExpectationError, fmt % [sym, args]
  end

  unless expected_kwargs.keys.sort == kwargs.keys.sort then
    fmt = "mocked method %p called with unexpected keywords %p vs %p"
    raise MockExpectationError, fmt % [sym, expected_kwargs.keys, kwargs.keys]
  end

  zipped_kwargs = expected_kwargs.to_h { |ek, ev|
    av = kwargs[ek]
    [ek, [ev, av]]
  }

  fully_matched = zipped_kwargs.all? { |ek, (ev, av)|
    ev === av or ev == av
  }

  unless fully_matched then
    fmt = "mocked method %p called with unexpected keyword arguments %p vs %p"
    raise MockExpectationError, fmt % [sym, expected_kwargs, kwargs]
  end

  @actual_calls[sym] << {
    :retval => retval,
    :args   => zipped_args.map { |e, a| e === a ? e : a },
    :kwargs => zipped_kwargs.to_h { |k, (e, a)| [k, e === a ? e : a] },
  }

  retval
end