17

I'm am adding tests to a Rails app that remotely stores files. I'm using the default Rails functional tests. How can I add file uploads to them? I have:

test "create valid person" do
  post(:create, :person => { :avatar => fixture_file_upload('avatar.jpeg') })
end

This for some reason uploads a Tempfile and causes the AWS/S3 gem to fail with:

NoMethodError: undefined method `bytesize' for Tempfile

Is their any way that I can get the test to use an ActionDispatch::Http::UploadedFile and perform more like it does when testing with the web browser? Is fixture_file_upload the way to test uploading files to a controller? If so why doesn't it work like the browser?

As a note, I really don't want to switch testing frameworks. Thanks!

Kevin Sylvestre
  • 37,288
  • 33
  • 152
  • 232

3 Answers3

59

I use the s3 gem instead of the aws/s3 gem. The main reasons for this are no support for european buckets and development of aws/s3 seems to be stopped.

If you want to test file upload than using the fixtures_file_upload method is correct, it maps directly to Rack::Test::UploadedFile.new (you can use this if the test file isn't in the fixtures folder).

But I've also noticed that the behavior of the Rack::Test::Uploaded file objects isn't exactly the same as the ActionDispatch::Http::UploadedFile object (that's the class of uploaded files). The basic methods (original_filename, read, size, ...) all work but there are some differences when working with the file method. So limit your controller to these methods and all will be fine.

An other possible solution is by creating an ActionDispatch::Http::Uploaded file object and using that so:

upload = ActionDispatch::Http::UploadedFile.new({
  :filename => 'avatar.jpeg',
  :type => 'image/jpeg',
  :tempfile => File.new("#{Rails.root}/test/fixtures/avatar.jpeg")
})

post :create, :person => { :avatar => upload }
ole
  • 5,166
  • 5
  • 29
  • 57
Stefaan Colman
  • 3,715
  • 2
  • 22
  • 11
  • Sorry for the delay but this looks perfect! Really appreciate the answer! – Kevin Sylvestre Jan 08 '11 at 07:59
  • 3
    +1, it's safer to use ActionDispatch::Http::UploadedFile because Rack::Test::Uploaded is not API compatible with Rails. – David Morales Mar 14 '12 at 09:43
  • 3
    The attribute is `content_type` but you need tho pass the key :type into the initializer. (http://stackoverflow.com/questions/23899860/actiondispatchhttpuploadedfile-content-type-not-being-initialized-in-rspec-t) – Anna Sep 03 '15 at 03:22
  • 2
    `upload ` comes as `"#"` string in params – Sohair Ahmad Feb 01 '17 at 13:53
  • I have the same problem as @sohair. This solution doesn't work, the file just comes to the controller as a string in the format he mentioned. – Ben Lee Mar 20 '17 at 23:08
  • ActionDispatch::Http::UploadedFile did not work for me but Rack::Test::UploadedFile did. (https://www.trimagency.com/blogs/testing-a-multipart-form-upload-in-rails-rspec/) – Rob Carpenter Oct 20 '20 at 09:47
0

I'd recommend using mocks. A quick google search reveals:

http://www.ibm.com/developerworks/web/library/wa-mockrails/index.html

You should be able to create an object that will respond to the behaviors you want it to. Mostly used in a Unit test environment, so you can test your stuff in isolation, as integration tests are supposed to fully exercise the entire stack. However, I can see in this case it'd be useful to mock out the S3 service because it costs money.

BeepDog
  • 5,016
  • 2
  • 24
  • 33
  • Thanks for the response but I'm really looking to stick with using plain functional tests if possible! and I am very comfortable paying the micro fees to test the S3 service (as it is core). – Kevin Sylvestre Dec 23 '10 at 17:06
0

I'm not familiar with the AWS/S3 gem, but it seems that you probably aren't using the :avatar param properly. bytesize is defined on String in ruby1.9. What happens if you call read on the uploaded file where you pass it into AWS/S3?

cam
  • 14,192
  • 1
  • 44
  • 29
  • 1
    I don't think this is the problem. My code works fine when testing in the browser. I just need to add support for automated testing and would like the tests to use an `ActionDispatch::Http::UploadedFile` (that responds to `tempfile`, giving access to the file on disk) so I can integrate. – Kevin Sylvestre Jan 03 '11 at 20:48