webmock
webmock copied to clipboard
Stub request with hash_including
I am trying to match this url
GET http://example.com/?a=1&b=2&x=1
but it fails. Here a reproducible error.
WebMock.disable_net_connect!
stub_request(:get, "example.com").with(query: hash_including({"x" => 1}))
Net::HTTP.get(URI('http://example.com/?a=1&b=2&x=1'))`
The error says:
Failure/Error: Net::HTTP.get(URI('http://example.com/?a=1&b=2&x=1'))
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: GET http://example.com/?a=1&b=2&x=1 with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Host'=>'example.com', 'User-Agent'=>'Ruby'}
You can stub this request with the following snippet:
stub_request(:get, "http://example.com/?a=1&b=2&x=1").
with(
headers: {
'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Host'=>'example.com',
'User-Agent'=>'Ruby'
}).
to_return(status: 200, body: "", headers: {})
registered request stubs:
stub_request(:get, "http://example.com/ with query params hash_including({"x"=>1})")
I found the problem is due to "integer" comparison vs "String"
This works. stub_request(:get, "example.com").with(query: hash_including({"a" => "1"})) p Net::HTTP.get(URI('http://example.com/?a=1&b=2&x=1'))
I think it makes sense to automatically cast everything to string before doing the Hash diff
I came across this issue yesterday and it took me a while to debug.
After playing around with the Webmock code, I don't think there's a clear solution to fix hash_including
directly—since it's defined in RSpec and changing it will undoubtably break other specs that use that method outside of Webmock params.
@bblimke Do you think it would be helpful to add a separate method for this purpose? Something like
def query_hash_including(*args)
args = args.map { |arg| arg.is_a?(Hash) ? cast_numeric_hash_values_as_strings(arg) : arg }
hash_including(*args)
end
def cast_numeric_hash_values_as_strings(hash)
hash.transform_values { |value| value.is_a?(Numeric) ? value.to_s : value }
end
That might allow Webmock users to use integers, floats, or strings in query param expectations, without breaking anything
stub_request(:get, "example.com").with(query: query_hash_including({"a" => "1"}))
@Bodacious thank you for providing the suggested solution.
I wish HashIncludingMatcher
in RSpec provided a public method to get the @expected
Hash.
We could then check if the Hash contains any non String values and raise an argument error, explaining the user,
to only match the query against a a hash with string values.
I feel it's more intuitive to use hash_including
rather than custom query_hash_including
. In addition to that people should learn, what is the difference, when they use query_hash_including.
Perhaps support for hash_including should be deprecated and deprecation message could explain that it needs to be replaced with query_hash_including