111

I have a controller which is responsible for accepting JSON files and then processing the JSON files to do some user maintenance for our application. In user testing the file upload and processing works, but of course I would like to automate the process of testing the user maintenance in our testing. How can I upload a file to a controller in the functional testing framework?

animal
  • 3,179
  • 3
  • 21
  • 17

7 Answers7

121

Searched for this question and could not find it, or its answer on Stack Overflow, but found it elsewhere, so I'm asking to make it available on SO.

The rails framework has a function fixture_file_upload (Rails 2 Rails 3, Rails 5), which will search your fixtures directory for the file specified and will make it available as a test file for the controller in functional testing. To use it:

1) Put your file to be uploaded in the test in your fixtures/files subdirectory for testing.

2) In your unit test you can get your testing file by calling fixture_file_upload('path','mime-type').

e.g.:

bulk_json = fixture_file_upload('files/bulk_bookmark.json','application/json')

3) call the post method to hit the controller action you want, passing the object returned by fixture_file_upload as the parameter for the upload.

e.g.:

post :bookmark, :bulkfile => bulk_json

Or in Rails 5: post :bookmark, params: {bulkfile: bulk_json}

This will run through the simulated post process using a Tempfile copy of the file in your fixtures directory and then return to your unit test so you can start examining the results of the post.

Jan Klimo
  • 4,643
  • 2
  • 36
  • 42
animal
  • 3,179
  • 3
  • 21
  • 17
  • 2
    This doesn't work on rails 3. The action_controller/test_process file is not available in activesupport 3 it seems. I have opened a new question for the same here - http://stackoverflow.com/questions/3966263/attachment-fu-testing-in-rails-3 – Chirantan Oct 19 '10 at 08:33
  • The link in your answer is 404, or is it just for me? – Ernest Dec 14 '10 at 11:46
  • @Chirantan it's moved to ActionDispatch::TestProcess – ben author Nov 16 '11 at 18:46
  • how can this be used with FactoryGirl ? http://stackoverflow.com/questions/34677780/using-fixture-file-upload-method-rails-4-rspec – Richlewis Jan 08 '16 at 13:40
90

Mori's answer is correct, except that in Rails 3 instead of "ActionController::TestUploadedFile.new" you have to use "Rack::Test::UploadedFile.new".

The file object that is created can then be used as a parameter value in Rspec or TestUnit tests.

test "image upload" do
  test_image = path-to-fixtures-image + "/Test.jpg"
  file = Rack::Test::UploadedFile.new(test_image, "image/jpeg")
  post "/create", :user => { :avatar => file }
  # assert desired results
  post "/create", :user => { :avatar => file }     

  assert_response 201
  assert_response :success
end
matiasmasca
  • 605
  • 8
  • 14
Victor Grey
  • 941
  • 6
  • 4
  • This method is a nice alternative when your fixtures are not located in /test/fixtures. If you generate dynamically some files for the tests for example, they might be in a /tmp folder instead. – Olivier Amblet Sep 11 '12 at 14:45
26

I think it's better to use the new ActionDispatch::Http::UploadedFile this way:

uploaded_file = ActionDispatch::Http::UploadedFile.new({
    :tempfile => File.new(Rails.root.join("test/fixtures/files/test.jpg"))
})
assert model.valid?

This way you can use the same methods you are using in your validations (as for example tempfile).

David Morales
  • 17,816
  • 12
  • 77
  • 105
8

From The Rspec Book, B13.0:

Rails’ provides an ActionController::TestUploadedFile class which can be used to represent an uploaded file in the params hash of a controller spec, like this:

describe UsersController, "POST create" do
  after do
    # if files are stored on the file system
    # be sure to clean them up
  end

  it "should be able to upload a user's avatar image" do
    image = fixture_path + "/test_avatar.png"
    file = ActionController::TestUploadedFile.new image, "image/png"
    post :create, :user => { :avatar => file }
    User.last.avatar.original_filename.should == "test_avatar.png"
  end
end

This spec would require that you have a test_avatar.png image in the spec/fixtures directory. It would take that file, upload it to the controller, and the controller would create and save a real User model.

Mori
  • 27,279
  • 10
  • 68
  • 73
  • 2
    I get a "uninitialized constant ActionController::TestUploadedFile" error when I run a test with this code. Anything else I would need to require in the file? – Vini.g.fer May 21 '15 at 18:21
  • 1
    It seems to be deprecated as early as Rails 2.3. For Rails 3.0 onwards, [here's how you do it](http://apidock.com/rails/ActionDispatch/TestProcess/fixture_file_upload) using fixtures – Sebastialonso Jul 08 '16 at 23:53
4

You want to use fixtures_file_upload. You will put your test file in a subdirectory of the fixtures directory and then pass in the path to fixtures_file_upload. Here is an example of code, using fixture file upload

Rob Di Marco
  • 43,054
  • 9
  • 66
  • 56
1

If you are using default rails test with factory girl. Fine below code.

factory :image_100_100 do
    image File.new(File.join(::Rails.root.to_s, "/test/images", "100_100.jpg"))
end

Note: you will have to keep an dummy image in /test/images/100_100.jpg.

It works perfectly.

Cheers!

Manish Shrivastava
  • 30,617
  • 13
  • 97
  • 101
0

if you are getting the file in your controller with the following

json_file = params[:json_file]
FileUtils.mv(json_file.tempfile, File.expand_path('.')+'/tmp/newfile.json')

then try the following in your specs:

json_file = mock('JsonFile')
json_file.should_receive(:tempfile).and_return("files/bulk_bookmark.json")
post 'import', :json_file => json_file
response.should be_success

This will make the fake method to 'tempfile' method, which will return the path to the loaded file.

Esenbek Kydyr uulu
  • 1,553
  • 1
  • 14
  • 19