Today I was working with logic around file uploads, and here's what I learned:
In request specs, to simulate a form submission of a file, you can use a Rack::Test::UploadedFile
object (see docs). Initializing it is a bit involved, so I recommend setting up a spec helper:
# in some place that gets loaded for specs like spec/support/helpers/uploaded_file_helper.rb
module UploadedFileSpecHelper
# Mimics a file upload parameter. Use like so:
# uploaded_file(fixtures_path("example.pdf"), "custom_name.pdf", "application/pdf")
def uploaded_file(path, name, mime_type, binmode: true)
Rack::Test::UploadedFile.new(path, mime_type, binmode, original_filename: name)
end
end
RSpec.configure do |config|
config.include UploadedFileSpecHelper, type: :request
end
Then setting up params in a request spec for some #create action is a breeze:
let(:params) do
{
upload: {
file: uploaded_file(fixtures_path("example.pdf"), "custom_name.pdf", "application/pdf"),
}
}
end
In the controller, Rails will have parsed this object into a regular ActionDispatch::Http::UploadedFile
object like in a real request. If this object is passed deeper into backend, for example, some service, for specs there you'll have to use this instead:
ActionDispatch::Http::UploadedFile.new(
filename: "custom_name.pdf",
type: "application/pdf",
tempfile: File.open(fixtures_path("example.pdf"))
)