Testing an Uploaded File in Rspec
Uploading a file without actually uploading it
People Apple Desk by Lee Campbell is licensed under CC0
I recently wrote a unit test for a bit of code that saves an uploaded file from a controller to the database before pushing it off to a job. I had an example file in spec/fixtures
that I wanted to use. Throwing code at the wall, I came up with:
# Does not work! file = File.new(Rails.root.join("spec/fixtures/filename.xlsx")) save_uploaded_file_to_db(file)
Then, #save_uploaded_file_to_db
gets the parts of the file that we care about, and saves it to the database.
def save_uploaded_file_to_db(file) Upload.create( filename: File.basename(file.original_filename), content_type: file.content_type, data: file.read, ) end
Looks pretty straightforward, but I got a NoMethodError
when calling original_filename
. Turns out, this method is unique to UploadedFile
. That brought me to the question: “How can I have an uploaded file without uploading something?” I have the answer for you.
Why not instantiate the uploaded file the way we did the file?
file = Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/filename.xlsx")) save_uploaded_file_to_db(file)
Well, that was easy, but it’s not very pretty. Thankfully, there’s a helper method we can use to clean it up. If you have ActionDispatch::TestProcess::FixtureFile
included, you can use fixture_file_upload
to create an upload file from a file in your test fixtures.
include ActionDispatch::TestProcess::FixtureFile file = fixture_file_upload("filename.xlsx") save_uploaded_file_to_db(file)
Easy enough. Then, you can use file
the way you were treating your uploaded file. Happy testing!
Comments
Yay! This helped so much!
Very informative. . .
Don’t include ActionDispatch::TestProcess. It has some nasty consequences.
Proper solution is to use
Rack::Test::UploadedFile
directly in the factory (whatfixture_file_upload
essentially does anyway):Amazing post here it is the very nice.