Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stub multipart requests with webmock/rspec

I have been trying for a while to stub multipart requests using webmock and have not found a satisfying solution.

Ideally, I would like to stub the request as follow:

stub_request(:post, 'http://test.api.com').with(:body => { :file1 => File.new('filepath1'), file2 => File.new('filepath2') })

However, this does not seem to work and RSpec complains that the request has not been stubbed. The non-stubbed request is printed:

stub_request(:post, "http://test.api.com").
     with(:body => "--785340\r\nContent-Disposition: form-data; name=\"file1\"; filename=\"filepath1\"\r\nContent-Type: text/plain\r\n\r\nhello\r\n--785340\r\nContent-Disposition: form-data; name=\"file2\"; filename=\"filepath2\"\r\nContent-Type: text/plain\r\n\r\nhello2\r\n--785340\r\n",
          :headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Content-Length'=>'664', 'Content-Type'=>'multipart/form-data; boundary=785340', 'User-Agent'=>'Ruby'}).
     to_return(:status => 200, :body => "", :headers => {})

Of course, I can't really follow this suggestion because the boundaries are generated dynamically. Any idea how I could properly stub these requests?

Thanks! Bruno

like image 650
Kamchatka Avatar asked Mar 13 '13 02:03

Kamchatka


3 Answers

Kind of late but I'll leave an answer for future overflowers and googlers.

I had the same problem and used Rack::Multipart::Parser in conjunction with Webmock as a work around. Quick and dirty code should look something like this (warning: uses activesupport extensions):

stub_request(:post, 'sample.com').with do |req|
  env = req.headers.transform_keys { |key| key.underscore.upcase }
                   .merge('rack.input' => StringIO.new(req.body))
  parsed_request = Rack::Multipart::Parser.new(env).parse

  # Expectations:
  assert_equal parsed_request["file1"][:tempfile].read, "hello world"
end
like image 194
camagu Avatar answered Oct 17 '22 19:10

camagu


WebMock doesn't support multipart requests at the moment. Check author's comment here for more info: https://github.com/vcr/vcr/issues/295#issuecomment-20181472

I suggest you consider one of the following routes:

  • stubbing without matching the post multipart body
  • wrapping the request in a method with file path arguments and setting more fine-grained expectation on this method
  • using VCR for mocking external requests in integration tests
like image 25
jurglic Avatar answered Oct 17 '22 19:10

jurglic


Here's a workaround using WebMock with regex to match multipart/form-data requests, especially handy for testing uploading of images:

stub_request(:post, 'sample.com').with do |req|

  # Test the headers.
  assert_equal req.headers['Accept'], 'application/json'
  assert_equal req.headers['Accept-Encoding'], 'gzip, deflate'
  assert_equal req.headers['Content-Length'], 796588
  assert_match %r{\Amultipart/form-data}, req.headers['Content-Type']
  assert_equal req.headers['User-Agent'], 'Ruby'

  # Test the body. This will exclude the image blob data which follow these lines in the body
  req.body.lines[1].match('Content-Disposition: form-data; name="FormParameterNameForImage"; filename="image_filename.jpeg"').size >= 1
  req.body.lines[2].match('Content-Type: img/jpeg').size >= 1

end

Testing only the headers could also have been done using the normal WebMock way of using stub_request(:post, 'sample.com').with(headers: {'Accept' => 'application/json}), and simply not include any body specification in the with clause.

like image 40
Magne Avatar answered Oct 17 '22 18:10

Magne