webmock: Response body can't be a hash

When I stub a request and specify a hash for the response body, I get the following error:

WebMock::Response::InvalidBody:
    must be one of: [Proc, IO, Pathname, String, Array]. 'Hash' given

Is there a particular reason why it can’t be a hash?

About this issue

  • Original URL
  • State: open
  • Created 9 years ago
  • Reactions: 37
  • Comments: 16 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Would it not be best to use the headers content-type to determine whether to return JSON or XML? I am stubbing as so

stub_request(:post,'http://......').
     to_return(
      :status => 200,
      :body => '{"transaction_status":"declined"}',
      :headers => {"Content-Type"=> "application/json"})

but would prefer not to have to call JSON.parse(response_body) since that is not a step that is required in my code as JSON is implicitly declared. Maybe I am missing something though? FWIW, I am using Faraday to make the actual request.

@pboling

HTTP response body is never a hash (there is no Hash content type). Your ruby HTTP client most likely does the response body parsing for you and converts is to a Hash. In order for your http client to know that the response body is JSON, you need to indicate that by the Content-Type header.

stub_request(:get, 'http://......').
     to_return(
      body: json_string,
      headers: {"Content-Type"=> "application/json"})

Thank you all for your answers, specially @joshuaswilcox and @bblimke. I was having issues with a spec for hours which mixed WebMock response with another gem expecting a hash. It wasn’t until I ended up here that I was able to work through it. Had to use both JSON.generate and Content-Type suggestions.

I’m leaving my solution here:

stub_request(:get, /exampleurl.com/)
  .to_return(
     status: 200,
     body: JSON.generate(response_json),
     headers: {"Content-Type"=> "application/json"}
   )

I am not sure that makes sense. If a server is actually returning JSON as the body content, the code parsing can assume its a Hash content type, therefore calling JSON.parse(response) will fail if its already a Hash. So its seems to me that Webmock should be able to send that as the response body, especially when a content-type of ‘application/json’ is explicitly passed.

I don’t think that is a problem to require a content-type when passing certain types of body content, since that is generally a good practice across http requests. Could i make a pull request for this functionality?

Most people will forget about headers.

Thank you so much @bblimke @joshuaswilcox @dduqueti.

Assuming response is a hash and I’m writing a Rails app I can simplify @dduqueti’s answer a little more using .to_json:

stub_request(:get, /exampleurl.com/)
  .to_return(
     status: 200,
     body: response.to_json,
     headers: {"Content-Type"=> "application/json"}
   )

Please have a look at https://github.com/bblimke/webmock/pull/427

If response body is declared as a hash, it’s not clear what should the hash be encoded to. JSON, XML, or perhaps url params? Therefore WebMock expects you to stringify hash to expected encoding. I.e {a: b}.to_json

I think part of what makes this particularly confusing is that if you pass an empty hash, it doesn’t throw an error.

In case there is response content type declared it could work. Webmock would have to raise an error is case body is declared as a hash and headers are not declared.