File Upload Form Testing Fixtures
Recently, I needed to write my first set of functional tests for a form that is used to upload image assets into our Content Management System. I wanted to find something as easy as fixtures for testing this part of our program.
Whether you are using Rick Olson’s excellent attachment_fu (see Mike Clark’s nice tutorial), Sebastian Kanthak and Jonas Nicklas’ upload_column, or your own code that works with file uploads, you should test this workflow just like any other code path. At first, I thought this was going to be a painful task and require messing around with some custom File I/O or other marshaling trickery. Most of the documentation for 3rd party file upload implementations tended to have little or no information on testing.
Then, while reading through the code a bit more, I stumbled upon fixture_file_upload. This convenience function is tucked away in ActionController’s TestProcess module and is shorthand for instantiating the underlying ActionController::TestUploadedFile class. TestUploadedFile itself is a mock object that simulates the target file that my user would upload in a form or other interface via a MIME multipart/form-data POST.
To use this handy method, I simply put test files in a convenient directory within my fixtures directory, say “files”. Then in a test for upload_column, I write:
def test_should_create_asset
old_count = Asset.count
post :create, :asset => { :title => "test", :file => fixture_file_upload('/files/testpicture1.jpg', 'image/jpeg') }
assert_equal old_count+1, Asset.count
assert_redirected_to asset_path(assigns(:asset))
end
Here, the :file attribute for my asset model is expecting an uploaded file from my form, so I feed it one from my fixtures/files directory.
If I use attachment_fu, I may write a create test:
def test_should_create_asset
old_count = Asset.count
post :create, :asset => { :name => 'railslogo', :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png') },
:html => {:multipart => true }
assert_equal old_count+1, Asset.count
end
or an update test:
def test_should_update_asset
put :update, {:id => assets(:one).id, :asset => { :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png') }}
assert_redirected_to asset_path(assigns(:asset))
end
The TestUploadedFile class is not limited to image data. It can channel any type I may wish into the request. Its default content type assumes text/plain.
Now I can proceed to write a suite of tests for my file upload use cases and let Action Controller and the fixture_file_upload helper do the heavy lifting.

Reader Comments
2 commentsAwesome!
From: Joe, 07/02/08 07:05 AM
I’ve been trying to figure out how to test file uploads for an hour now and I stumbled upon this. Great job!
Nice Article
From: Ziemek, 04/29/08 01:44 AM
short sweet and to the point. Well done!