webmock icon indicating copy to clipboard operation
webmock copied to clipboard

`hash_including` matcher breaks `WebMock::Util::Headers.normalize_headers`

Open cosmicbuffalo opened this issue 4 years ago • 6 comments

came across this while attempting to put an expectation on a mocked request's headers

example test to reproduce:


RSpec.describe "webmock broken normalize_headers test" do
  it 'should work' do
    WebMock.stub_request(:post, /foo/).to_return(body: proc{|_| {}.to_json })

    Faraday.post('http://www.foo.com', {foo: :bar}.to_json, "Content-Type" => 'application/json', "CUSTOM_HEADER" => "value")

    expect(WebMock).to have_requested(:post, 'http://www.foo.com').with(
      headers: hash_including({
        "CUSTOM_HEADER" => "value"
      })
    )
  end
end

This syntax looks like it should work fine judging by the examples in the readme:

Screen Shot 2021-05-26 at 9 37 11 AM

But instead, the above example test spits out the following:

Failures:

  1) webmock broken normalize_headers test should work
     Failure/Error:
       expect(WebMock).to have_requested(:post, 'http://www.foo.com').with(
         headers: hash_including({
           "CUSTOM_HEADER" => "value"
         })
       )
     
     NoMethodError:
       undefined method `map' for #<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x0000000111c45dd0>
       Did you mean?  tap
     # ./vendor/bundle/ruby/2.6.0/gems/webmock-3.13.0/lib/webmock/util/headers.rb:11:in `normalize_headers'
     # ./vendor/bundle/ruby/2.6.0/gems/webmock-3.13.0/lib/webmock/request_pattern.rb:375:in `initialize'
     # ./vendor/bundle/ruby/2.6.0/gems/webmock-3.13.0/lib/webmock/request_pattern.rb:60:in `new'
     # ./vendor/bundle/ruby/2.6.0/gems/webmock-3.13.0/lib/webmock/request_pattern.rb:60:in `assign_options'
     # ./vendor/bundle/ruby/2.6.0/gems/webmock-3.13.0/lib/webmock/request_pattern.rb:28:in `with'
     # ./vendor/bundle/ruby/2.6.0/gems/webmock-3.13.0/lib/webmock/rspec/matchers/webmock_matcher.rb:35:in `with'
     # ./spec/lib/proxy/rspec_spec.rb:8:in `block (2 levels) in <top (required)>'
     # ./vendor/bundle/ruby/2.6.0/gems/webmock-3.13.0/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'

cosmicbuffalo avatar May 26 '21 14:05 cosmicbuffalo

@cosmicbuffalo hash_including doesn't work for headers, but matching headers behaves like hash_including by default.

bblimke avatar May 26 '21 15:05 bblimke

"matching headers behaves like hash_including by default" that is not working for me, when I try to match partially the headers, it does not work :(

leodeoliveirasilva avatar Nov 27 '23 20:11 leodeoliveirasilva

"matching headers behaves like hash_including by default" that is not working for me, when I try to match partially the headers, it does not work :(

can you please provide an example if it's still relevant?

bblimke avatar Feb 05 '24 12:02 bblimke

Not working for me either. To reproduce:

describe "google" do
  it "test" do
    stub_request(:get, "https://google.com").with(headers: hash_including({})).to_return(body: "")
    Faraday.new.get("https://google.com") do |req|
      req.headers = { "Not-Included-In-Stub": "true" }
    end
    expect(WebMock).to have_requested(:get, 'https://google.com')
  end
end

Outcome:

Screenshot 2024-02-22 at 12 56 37 PM

I need to be able to do this so that I can partially match headers. Right now is all or nothing.

If I remove the hash_including it simply fails because it doesn't match:

 it "test" do
    stub_request(:get, "https://google.com").with(headers: {}).to_return(body: "")
    Faraday.new.get("https://google.com") do |req|
      req.headers = { "Not-Included-In-Stub": "true" }
    end
    expect(WebMock).to have_requested(:get, 'https://google.com')
end

Outcome:

Screenshot 2024-02-22 at 1 04 35 PM

The reason I'm passing an empty hash {} is because it's the default value of a wrapping function.

mochetts avatar Feb 22 '24 15:02 mochetts

@mochetts thank you for reporting. When with(headers: {}) is provided, it doesn't match and it should. I suggest removing with(headers: {}) completely. from your code as a temporary solution.

bblimke avatar Feb 23 '24 09:02 bblimke

What I ended up doing is this: https://github.com/moraki-finance/docuseal/blob/main/spec/spec_helper.rb#L29

Not ideal, but works.

mochetts avatar Feb 23 '24 10:02 mochetts