webmock
webmock copied to clipboard
`uri` method on mocked `Net::HTTPOK` incorrectly returns nil
require 'webmock'
include WebMock::API
WebMock.allow_net_connect!
target_uri = URI.parse('http://www.google.com')
real_request = Net::HTTP.get_response(target_uri) # => #<Net::HTTPOK 200 OK readbody=true>
real_request.uri # => #<URI::HTTP http://www.google.com>
WebMock.disable_net_connect!
stub_request(:get, "http://www.google.com/").
to_return(:status => 200, :body => "", :headers => {})
mocked_request = Net::HTTP.get_response(target_uri) # => #<Net::HTTPOK 200 readbody=true>
mocked_request.uri # => nil
I believe the method is inherited from HTTPResponse#uri.
This problem occurred on Ruby 2.2.0 and webmock version 1.21.0 as well as master (currently 9e7162f6).
@abevoelker thank you for reporting that. I'd need to investigate more to find out what the root cause is, unless someone investigates that before me.
No rush, I was able to avoid it by restructuring my code. Thanks for the neat library!
I have something similar going on in my Net::HTTP wrapper.
I attach the last_response instance variable to the response body to know the last resolved url in case of redirects. And it seems to have uri unset:
(byebug) head.instance_variable_get(:@last_response)
#<Net::HTTPOK 200 readbody=true>
(byebug) head.instance_variable_get(:@last_response).uri
nil
UPD: maybe somewhere here https://github.com/bblimke/webmock/blob/d03e76ae41f6d7fbbe31ff45cb077234de4f94aa/lib/webmock/http_lib_adapters/net_http.rb#L83 it should add uri to the response.
Or maybe response.uri = @uri here: https://github.com/bblimke/webmock/blob/d03e76ae41f6d7fbbe31ff45cb077234de4f94aa/lib/webmock/http_lib_adapters/net_http.rb#L179
Still there is a problem after this edit that if there is a chain of 302 redirects, the .uri gives me the 2nd of 4 uris. Seems like the @uri variable changes by the first redirect Location and does not change afterwards:
p response.uri = @uri
stub_request(:head, "https://flic.kr/p/vPvCWJ"). to_return status: [302, "Found"], headers: {location: "https://www.flickr.com/photo.gne?short=vPvCWJ"}
stub_request(:head, "https://www.flickr.com/photo.gne?short=vPvCWJ"). to_return status: [302, "Found"], headers: {location: "/photo.gne?rb=1&short=vPvCWJ"}
stub_request(:head, "https://www.flickr.com/photo.gne?rb=1&short=vPvCWJ").to_return status: [302, "Found"], headers: {location: "/photos/mlopezphotography/19572004110/"}
stub_request(:head, "https://www.flickr.com/photos/mlopezphotography/19572004110/")
#<URI::HTTPS https://flic.kr/p/vPvCWJ>
#<URI::HTTPS https://www.flickr.com/photo.gne?short=vPvCWJ>
#<URI::HTTPS https://www.flickr.com/photo.gne?short=vPvCWJ>
#<URI::HTTPS https://www.flickr.com/photo.gne?short=vPvCWJ>
Not sure yet if this behaviour is in webmock or on my side but I know for sure that it was ok (and I had the 4th uri in the end) in my tests before webmock. Maybe @uri is just a wrong thing to assign.
UPD2: it works if I assign the request.uri instead of @uri. The monkey patch is:
WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get(:@webMockNetHTTP).class_eval do
old_request = instance_method :request
define_method :request do |request, &block|
old_request.bind(self).(request, &block).tap do |response|
response.uri = request.uri
end
end
end
Is this something that could get made into a PR and merged in? I just hit it in a code where I want to follow redirects and update the model's url to the last one. Monkey patch still works, but feels like this should be fixed in core, no?
FWIW, I built a test project that reproduces the nil uri behavior: https://github.com/Automattic/webmock-uri-demo.
@Nakilon's monkey-patching suggestion worked for me. Thanks! RuboCop suggested a slight change, implementing the lambda call rule:
# Monkey-patch WebMock to work around response.uri not being set.
# See https://github.com/bblimke/webmock/issues/469
WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get(:@webMockNetHTTP).class_eval do
old_request = instance_method :request
define_method :request do |request, &block|
old_request.bind(self).call(request, &block).tap do |response|
response.uri = request.uri
end
end
end
The fix is now released in version 3.21.1