I recently had the opportunity to make a simple JSON web service using Sinatra, and while testing it I came across something that wasn't apparent from the documentation. I needed to test the web service's response when JSON was POST'ed to an endpoint. The obvious way to do this, or so I thought, would be to use the post() method like so:

post('/endpoint', { 'j': 's', 'o': 'n' })

Turns out that doesn't actually work the way one would expect. Looking at the source code on GitHub, it looks to me that the above code results in the Content Type of application/x-www-form-urlencoded and the Hash being URL encoded. It seems that if you pass a Hash at all Rack::Test will try to URL encoded the values. And as long as you set a specific Content Type Rack::Test won't mess with it. Something like the following will successfully result in a request with application/json for the Content Type and have the JSON as the request body.

post('/endpoint', "{ 'j': 's', 'o': 'n' }", { "CONTENT_TYPE" => "application/json" })

This is a bit more to type but it gets the job done, you can write a helper to wrap this into something shorter like:

def post_json(uri, json)
  post(uri, json, { "CONTENT_TYPE" => "application/json" })
end

I have yet to find another way to achieve this so I am open to any suggestions.