Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mention ability to specify chunks in the README #629

Open
janko opened this issue Jun 21, 2016 · 9 comments
Open

Mention ability to specify chunks in the README #629

janko opened this issue Jun 21, 2016 · 9 comments

Comments

@janko
Copy link
Contributor

janko commented Jun 21, 2016

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
Copy link
Owner

bblimke commented Jun 21, 2016

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
Copy link
Contributor Author

janko commented Jun 21, 2016

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
Copy link
Contributor Author

janko commented Jun 22, 2016

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
Copy link

pjg commented Oct 10, 2023

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
Copy link

@pjg thanks a lot, save my day!

@SamSaffron
Copy link

🤗 thanks, very helpful wish this was built in.

@SamSaffron
Copy link

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 added a commit to SamSaffron/webmock that referenced this issue Mar 19, 2024
Per bblimke#629, this adds support for body stream mocking.

It is only implemented on Net::HTTP for now as this is the most
surgical change we can make without impacting the entire framework.
@SamSaffron
Copy link

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

@yoka
Copy link

yoka commented Aug 20, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants