Most of the time in my tests I mock out all external resources, e.g. file
systems, network I/O, databases, etc. I recently discovered the
#fixture_file_upload
method that’s available in Rails tests. File upload was
one area I always mocked out because I didn’t even know how to do a file upload
in a functional test. With this helper method you can do regular state-based
testing with file uploads.
Say you got a User
who’s got an image along side all the normal User
attributes.
Schema:
users (id, email, password)
We’ll store the image on the file system:
class User < ActiveRecord::Base
def image=(image)
file_name = File.join IMAGES_PATH, image.original_filename
File.open(file_name, 'wb') do |file|
file.puts image.read
end
end
end
Our UsersController
looks the same as any other simple CRUD controller.
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new params[:user]
if @user.save
redirect_to user_path(@user)
else
render :action => :new
end
end
end
Now let’s take a look at our functional test.
def test_should_create_a_new_user_record_on_POST_to_create
post :create, :user => {
:email => 'pepper@boloco.com',
:password => 'burritos',
:image => fixture_file_upload('images/boloco.gif', 'image/gif') }
assert File.exists?(File.join(IMAGES_PATH, 'boloco.gif'))
assert_response :redirect
assert_redirected_to user_path(assigns(:user))
assert_recognizes({ :controller => 'users',
:action => 'create' },
:path => 'users', :method => :post)
end
The helper method #fixture_file_upload
will look for the image file relative
to ‘test/fixtures’. I decided to create an ‘images’ subdirectory there just to
keep it straightforward (as long as I don’t have an Image
model). The method
takes a filename and its MIME type. After the POST
I assert the file exists.
The IMAGES_PATH
constant is environment specific and defined in
‘config/environments/test.rb’ as:
IMAGES_PATH = File.join RAILS_ROOT, 'tmp', 'images'
You might want to throw the following line in your functional test’s #setup
method to clean up your IMAGES_PATH
directory:
FileUtils.rm_rf Dir["#{IMAGES_PATH}/*"]
I don’t know if I’d ever write tests like this but mocking has been burning me lately a bunch so it’s good to know I can do state-based file upload testing.