webmock icon indicating copy to clipboard operation
webmock copied to clipboard

Is there a way to do hash_excluding matching?

Open x-yuri opened this issue 10 years ago • 7 comments

Like so:

require 'webmock'
include WebMock::API
stub_request(:post, 'http://example.com/path')
  .with(body: hash_excluding({a: '1'}))
  .to_return(body: 'b1')
puts Net::HTTP.post_form(URI('http://example.com/path'), {b: '2'}).body

rspec has this matcher, but it doesn't seem to work for webmock.

x-yuri avatar Jan 01 '15 21:01 x-yuri

For now there's this workaround for ruby 2.0+:

module BodyPatternExtensions
  def matches?(body, content_type = "")
    if @pattern.instance_of?(RSpec::Mocks::ArgumentMatchers::HashExcludingMatcher)
      @pattern === body_as_hash(body, content_type)
    else
      super
    end
  end
end

module WebMock
  class BodyPattern
    prepend BodyPatternExtensions
  end
end

x-yuri avatar Jan 01 '15 23:01 x-yuri

@x-yuri There is no way to do it, unless you use custom matching block.

Nice workaround though.

The proper solution would require adding hash_excluding matcher to WebMock, to make it independent from RSpec.

Perhaps it's worth adding a change to WebMock and allow it to accept any kind of RSpec::Mocks::ArgumentMatchers::* matcher.

bblimke avatar Jan 02 '15 09:01 bblimke

There is no way to do it, unless you use custom matching block.

By custom matching block you must be meaning this:

stub_request(:post, 'http://example.com/path')
  .with { |request| hash_excluding(a: '1') === Rack::Utils.parse_nested_query(request.body) }

Which brings the question, if I can call with several times to not end up with only custom matching.

Perhaps it's worth adding a change to WebMock and allow it to accept any kind of RSpec::Mocks::ArgumentMatchers::* matcher.

But there are not only hash matchers there. How will you know what to pass to any one of them?

x-yuri avatar Jan 02 '15 12:01 x-yuri

:+1:

This is a pain for places where you want to ensure that a given header is not present, since webmock will match any superset of the expected values. (i.e. trying to do the opposite case from #276)

ab avatar Jun 05 '16 21:06 ab

Ran into this issue today - guessing it's still awaiting a PR, as it hit me too, returning a

undefined method map for #<RSpec::Mocks::ArgumentMatchers::HashExcludingMatcher

(mostly leaving this comment to add to the google-ability of this line to get to this issue)

Will try the workaround for now... and sadly it doesn't seem to work - the above issue still gets triggered. Guess the monkey patch above is a few years old now :)

phantomwhale avatar Feb 14 '19 04:02 phantomwhale

In a similar situation where I'm trying to ensure the abscence of a certain header, rather than its presence - not seeing a nice way to go about it.

Kevin-McGonigle avatar Oct 26 '22 13:10 Kevin-McGonigle

I was facing a similar issue Browsing the code I found it was not working because when the registered stubs are being tested for a match HashExcludingMatcher (that is created by hash_excluding) is not handling the request body as a Hash, which is the behavior for HashIncludingMatcher (that works)

I opened this Pull Request as a suggestion to fix this issue: https://github.com/bblimke/webmock/blob/833291d4ce2d5927a738f929db26b594bf4fa7f5/lib/webmock/request_pattern.rb#L286

lucasarnaud avatar Dec 14 '22 23:12 lucasarnaud