bblimke / webmock

Library for stubbing and setting expectations on HTTP requests in Ruby.
MIT License
3.93k stars 553 forks source link

Mention ability to specify chunks in the README #629

Open janko opened 8 years ago

janko commented 8 years ago

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! ❤️

bblimke commented 8 years ago

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.

janko commented 8 years ago

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.

janko commented 8 years ago

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
pjg commented 9 months ago

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
ShallmentMo commented 7 months ago

@pjg thanks a lot, save my day!

SamSaffron commented 6 months ago

:hugs: thanks, very helpful wish this was built in.

SamSaffron commented 3 months ago

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...

SamSaffron commented 3 months ago

For the record... AI was sadly zero help here... made a PR :)