Webmock: Response body can't be a hash

Created on 30 Jan 2015  ·  14Comments  ·  Source: bblimke/webmock

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?

Enhancement Open PR Paramater Matching Upgrade

Most helpful comment

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.

All 14 comments

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

(Sorry for the late response, and thanks for your reply.)

I see, that makes sense. Maybe this should be mentioned in the Readme?

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.

"If a server is actually returning JSON as the body content" - which server do you mean? We are returning a stubbed response. This response will be passed through specific http client library internals. You can't expect these libs to handle Hash.

WebMock would have to convert Hash declared in to_return to json string automatically first, but how would it know whether to convert this Hash to json or to xml?

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.

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.

Most people will forget about headers.

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?

I don't think it's a problem either, as long as user gets a clear error message explaining content type needs to be added if missing.
Pull request would be most appreciated :)

Great, thanks!

I support and encourage implementation of this feature.

I'm surprised this isn't a more widespread issue. I've already encountered this issue on two separate occasions with two different web services. Most recently I needed to implement a work-around whereby I changed my response-parsing method to handle/convert the stubbed response strings when in reality outside of the test environment this extra handling would not be required.

I am running into this now. How is one supposed to mock a JSON response where the response is a hash? Not a JSON encoded string, but a hash. At the Ruby (Rails) layer the response is a hash in my real code, so it must also be in my test.

@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. I wasn't until I ended up here that I was able to work it through. Had to use both JSON.generate and Content-Type suggestions.

I'm leaving my solution for other readers:

stub_request(:get, /exampleurl.com/)
  .to_return(
     status: 200,
     body: JSON.generate(response_json),
     headers: {"Content-Type"=> "application/json"}
   )
Was this page helpful?
0 / 5 - 0 ratings

Related issues

bootstraponline picture bootstraponline  ·  18Comments

Integralist picture Integralist  ·  5Comments

vangberg picture vangberg  ·  3Comments

wgordon17 picture wgordon17  ·  5Comments

joallard picture joallard  ·  4Comments