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!

Photo of Victoria Gonda

Victoria is a software developer working on mobile and full stack web applications. She enjoys exchanging knowledge through conference talks and writing.

Comments

  1. Melissa
    February 13, 2017 at 17:47 PM

    Yay! This helped so much!

  2. July 06, 2017 at 7:06 AM

    Very informative. . .

  3. November 15, 2018 at 13:32 PM

    Don’t include ActionDispatch::TestProcess. It has some nasty consequences.

    Proper solution is to use Rack::Test::UploadedFile directly in the factory (what fixture_file_upload essentially does anyway):

    file { Rack::Test::UploadedFile.new('spec/factories/test.png', 'image/png') }
    
  4. January 27, 2019 at 9:36 AM

    Amazing post here it is the very nice.