For testing, I send a Rack::Request straight to the app, not using the server.
def request_via_API( app, method, path, params={} ) # app should be API
env = Rack::MockRequest.env_for( path, {:method => method, :params=>params} )
app.handle Rack::Request.new(env)
end
works great for testing direct input, but I'm stymied by file upload. My real system works great from the browser with a file upload. But now I want to test it via the API, and don't know how to get the file contents into the request via any of the Rack classes/methods. (I tried making sense of Rack::Test::UploadedFile but didn't succeed).
Thanks, Alistair
You were definitely on the right path. You can even use your function request_via_API
without any modifications, e.g.:
request_via_API(app, 'POST', '/', {
:field => "value",
:text_source => Rack::Multipart::UploadedFile.new(PATH_TO_YOUR_FILE, MIME_TYPE_OF_YOUR_FILE)
})
This means you need to have some file somewhere. If you use fixtures, your test upload file should be around them. You can omit MIME time, but it defaults to text/plain
.
If you use barebones Rack, you get the following hash after calling Rack::Multipart.parse_multipart
:
{
"field" => "value",
"text_source" => {
:filename => File.basename(PATH_TO_YOUR_FILE),
:type => MIME_TYPE_OF_YOUR_FILE,
:name => "text_source",
:tempfile => Tempfile.new("RackMultipart"), # copied from PATH_TO_YOUR_FILE
:head => "Content-Disposition: form-data; name=\"text_source\"; filename=\"#{File.basename(PATH_TO_YOUR_FILE)}\"\r\n" +
"Content-Type: #{MIME_TYPE_OF_YOUR_FILE}\r\n" +
"Content-Length: #{BYTESIZE_OF_YOUR_FILE}\r\n"
}
}
The text_source
key can have any other name, of course.
Rack::MockRequest#env_for
automatically tries to create multipart form data request if:
:input
option provided:params
option is a Hash
:params
option values contain at least one instance of Rack::Multipart::UploadedFile
You can see the details in the source code here and here.
I think relying on multipart request generation by Rack::MockRequest
and Rack::Multipart
is useful only for mocking HTML forms with file upload and file upload mechanisms that act the same. So, there's no need to use Rack::Multipart#build_multipart
or Rack::Multipart::Generator
directly.
If you have more complicated multipart scenarios or different file upload mechanism, you must pass opts
argument with :input
key instead of :params
to Rack::MockRequest#env_for
. How you generate that value for :input
is your problem as far as Rack mocking capabilities are concerned. It only wraps it in StringIO
if it is a String
as you can see here. Otherwise, it is the same thing that will be passed as rack.input
in the Rack environment hash and therefore it must conform to the Rack input stream spec (i.e., be an IO
-like object).
Because this was also quite a challenge for me and I used it as an exercise to deepen my knowledge of Rack I created a simple project on GitHub to explore this file upload mocking.
Note: I tried to fix everything to Rack 1.5.2 except for the link to the Rack SPEC (so beware). Links to Ruby StdLib lead to the current version.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With