Mention ability to specify chunks in the README
With WebMock I needed to test streaming response body with net/http, so I wanted to set specific chunks which will be yielded when I run the following code:
uri = URI("http://example.com")
Net::HTTP.get_response(uri) do |response|
response.read_body { |chunk| ... }
end
I was reading a bit of WebMock source code, but I couldn't find anything that might indicate existence of this feature. So at the end I just tried out the following, knowing that it probably wouldn't work:
stub_request(:get, "http://example.com").to_return(body: ["a", "b", "c"])
But it worked! This made me really happy! I just thought it would be great to document this feature in the README.
Absolutely awesome library, thank you! ❤️
wow, I never thought of that! nice :)
This can be documented as long as we add an acceptance spec to cover that scenario and it passes across all supported http clients.
Actually, I misread, it only yields only one chunk equal to ["a", "b", "c"] (I used puts instead of p, so I thought I was getting the right result). Maybe this could be a feature, but I guess it would take a lot of work to port it to all other adapters.
In the meanwhile I managed to create a workaround, so posting here in case anyone's interested:
before do
mocked_http = Class.new(Net::HTTP) do
def request(*)
super do |response|
response.instance_eval do
def read_body(*, &block)
@body.each_char(&block)
end
end
yield response if block_given?
response
end
end
end
@original_net_http = Net.send(:remove_const, :HTTP)
Net.send(:const_set, :HTTP, mocked_http)
end
after do
Net.send(:remove_const, :HTTP)
Net.send(:const_set, :HTTP, @original_net_http)
end
A slightly modified @janko's example from above, which avoids instance variable in spec, and expects stubbed body to be an array, where each array element will yield the the block passed to read_body:
let(:mock_net_http) do
Class.new(Net::HTTP) do
def request(*)
super do |response|
response.instance_eval do
def read_body(*, &)
@body.each(&)
end
end
yield response if block_given?
response
end
end
end
end
let(:remove_original_net_http) { Net.send(:remove_const, :HTTP) }
let(:original_http) { remove_original_net_http }
let(:stub_net_http) { Net.send(:const_set, :HTTP, mock_net_http) }
let(:remove_stubbed_net_http) { Net.send(:remove_const, :HTTP) }
let(:restore_net_http) { Net.send(:const_set, :HTTP, original_http) }
before do
mock_net_http
remove_original_net_http
stub_net_http
# Stub response body as an array of chunks
stub_request(:post, "OpenAI url").to_return(status: 200, body: ["first chunk", "second chunk"])
end
after do
remove_stubbed_net_http
restore_net_http
end
@pjg thanks a lot, save my day!
:hugs: thanks, very helpful wish this was built in.
hopes and prayers :) https://meta.discourse.org/discourse-ai/ai-bot/shared-ai-conversations/ZRd74GXAe7KhS1pUqcQ3nw
Will try to wire up a PR tomorrow... who knows how well this will do...
For the record... AI was sadly zero help here... made a PR :)
Great, thanks!
These hacks got me on the right track to cook up something that works for my environment (ruby 3.3.4, HTTParty 0.22,, rspec 3.13, webmock 3.23) at the moment.
I wanted to keep the hack at my spec_helper instead of inlining in suites. This and/or versions might have caused some differentiating factors on what I had to do. This implementation prolly breaks some things like the case for what the WebMockHTTPResponse module exists in the first place, but now that I can toggle it for specific tests it's good enough for my use case for now.
For what it's worth, here's what I had to put together to get my mocks rocking:
spec_helper.rb
# patch Net::WebMockHTTPResponse to support chunked responses
config.around(:example, :chunked_response) do |example|
original_method = Net::WebMockHTTPResponse.instance_method(:read_body)
Net::WebMockHTTPResponse.send(:define_method, :read_body) do |*args, &block|
@body.each(&block)
end
example.run
Net::WebMockHTTPResponse.send(:define_method, :read_body, original_method)
end
somethinsomethingdarkside_spec.rb
describe "response" do
let!(:endpoint) { "http://example.com/foshizzlez" }
let!(:chunks) { %w(it was all a dream used to read word up magazine) }
before(:each) do
stub_request(:post, "http://example.com/foshizzlez").with({body: "holla"}).to_return(status: 200, body: chunks)
end
it "is chunky", chunked_response: true do
dada = ""
HTTParty.post("http://example.com/foshizzlez", body: "holla") do |fragment|
dada += fragment
end
expect(dada).to eq(chunks.join)
end
end
I hope it helps someone on a similar quest.