How do I test a file upload in rails? - ruby-on-rails

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?

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.

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

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).

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.

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

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!

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.

Related

How to test external request with Ministest / TestCase out of controller scope

I'm new with Minitest particularly, at the moment everything goes ok because is not really hard to learn, but, I'm stuck with a routinary test: test a controller that upload a file to S3.
Goal:
Have a test that create a new Person.create() object with its file, in this case is a zip with some images.
Context:
I've a model Person with a file field with Paperclip and its configuration for S3.
I've various tests files for that (in tests/models & tests/controllers of course), but, with one more test in another folder because I'm testing other class that uses the Person object to update it.
My problem is that I've tried a lot of approach searching in Google and StackOverflow but I'm not sure how to address this tests out of the Controller scope.
require 'test_helper'
require 'webmock/minitest'
class PersonPhotosUpdateTest < ActiveSupport::TestCase
def setup
# some setup here
end
describe "My tests" do
test "Upload a zip file for Person" do
# My test here
end
end
end
I want in my test:
Create a new Person with post :create.
Person should be created with the file uploaded to S3 associated.
Assert that the file was uploaded to S3.
I suppose I need mock and/or stub for that but I'm not sure how with Minitest.
Thank you.
You may want to create an integration test:
require 'test_helper'
require 'webmock/minitest'
class LocationImporterTest < ActionDispatch::IntegrationTest
before do
stub_request(:any, "https://s3.amazonaws.com")
end
test "create" do
post "/foo", {
# params...
}
assert_requested :post, "https://s3.amazonaws.com",
:headers => {'Content-Length' => 3},
:body => "abc",
:times => 1 # ===> Success
end
end
See the webmock documents for how set expectations on the HTTP request, note that Test/Unit and Minitest are interchangable.

Testing if a file uploads sucessfully in Rails

I'm running Rails 3.2.13.
In one of my controllers, the application processes a form by which the users sends a file.
The application analyses the file and saves it OR NOT, depending on the analysis.
I want to write a functional test which tests that the correct file gets saved, and that the wrong file doesn't get saved.
I'm loading the file from test/fixtures/files, like this:
resource_file = Rack::Test::UploadedFile.new(Rails.root.
join('test', 'fixtures', 'files', 'f1.jpg'))
When running the test, where does this file gets saved?
/spec/support/fixture_file.rb
include ActionDispatch::TestProcess
def fixture_file(filename, extension)
fixture_file_upload(Rails.root.join('spec', 'fixtures', filename), extension)
end
/spec/factories/offers.rb
FactoryGirl.define do
factory :offer do
large_picture { fixture_file("ostrov.jpg", "image/jpeg") }
end
end
P.S It also works for TestUnit

Rails - Functional test failing because of validation

I can't for the life of me figure out what's going wrong here. This is the situation:
I have a document model using paperclip for pdf attachments
The functional test uploads a document and fails
The reason for this is my validation that it is a pdf file - somehow that validation fails
However, the file is a pdf and the validation should not fail
The validation only fails in the test - doing it manually by uploading the file it complains about works absolutely fine
Here is my failing test (the count is not increased by one):
test "should create document" do
assert_difference('Document.count') do
post :create, document: { pdf: fixture_file_upload("../files/document_test_file.pdf"), language: #document.language, published_on: #document.published_on, tags: #document.tags, title: #document.title, user_id: #user }
end
assert_redirected_to document_path(assigns(:document))
end
This is my validation in the document model:
def document_is_a_pdf
if !self.pdf.content_type.match(/pdf/)
errors.add(:pdf, "must be a pdf file")
false
end
end
If I do not call that validation in the model, the test runs fine. What am I doing wrong here?
I know this is an old question but if anyone still needs help,
From http://apidock.com/rails/ActionController/TestProcess/fixture_file_upload
fixture_file_upload(path, mime_type = nil, binary = false)
Fixture file upload method by default will set the mime type as nil so simply changing the mime type as below will correct this
fixture_file_upload("../files/document_test_file.pdf", 'application/pdf')
I found the problem. Somehow, while testing, the content type could not be determined. That is why the validation of the content type failed and the test did not pass.
I added the content type to the accessible attributes inside my document model and inserted the content type in the test (second attribute inside the document hash):
test "should create document" do
assert_difference('Document.count') do
post :create, document: { pdf: fixture_file_upload("../files/document_test_file.pdf"), pdf_content_type: "application/pdf", language: #document.language, published_on: #document.published_on, tags: #document.tags, title: #document.title, user_id: #user }
end
assert_redirected_to document_path(assigns(:document))
end

Writing functional tests for facebooker controller?

Anyone have any tips for best practices for mocking out facebook requests in functional tests? Is it just as simple as adding all of the proper params to the request? Is there a way to stub those out?
I'm using facebooker, which comes with a mock service:
# A mock service that reads the Facebook response from fixtures
# Adapted from http://gist.github.com/44344
#
# Facebooker::MockService.fixture_path = 'path/to/dir'
# Facebooker::Session.current = Facebooker::MockSession.create
But when I write a basic get test, it tries to redirect the browser to the facebook page for adding the app, which I assume indicates that the mocking isn't working.
test "loads respondent" do
Facebooker::Session.current = Facebooker::MockSession.create
get :index
puts #response.body # => <html><body>You are being redirected.</body></html>
end
I got this working with the latest version of facebooker (1.0.58):
# test_helper.rb
require 'facebooker/mock/session'
require 'facebooker/mock/service'
Facebooker::MockService.fixture_path = File.join(RAILS_ROOT, 'test', 'fixtures', 'facebook')
Obviously you will have to create the facebook directory in fixtures, or put it wherever. Inside you have to add a folder for each facebook method, and an xml file for the different types of responses you want to test for. I had to add facebook.users.getInfo and facebook.users.hasAppPermission. The easiest is just to add a file named default.xml with the example code from the facebook wiki for those actions.
# Controller test
test "facebook action" do
get :index, {:fb_sig_added => true}, :facebook_session => Facebooker::MockSession.create
assert_response :success
end
The fb_sig_added param is necessary as far as I can tell, because the internal facebooker logic checks the params directly before checking the session on that one. Which seems a bit wanky to me but maybe there's a reason for that.

Unit testing paperclip uploads with Rspec (Rails)

Total Rspec noob here. Writing my first tests tonight.
I've got a model called Image. Using paperclip I attach a file called photo. Standard stuff. I've run the paperclip generator and everything works fine in production and test modes.
Now I have a spec file called image.rb and it looks like this (it was created by ryanb's nifty_scaffold generator):
require File.dirname(__FILE__) + '/../spec_helper'
describe Image do
it "should be valid" do
Image.new.should be_valid
end
end
This test fails and I realise that it's because of my model validations (i.e. validates_attachment_presence)
The error that I get is:
Errors: Photo file name must be set., Photo file size file size must be between 0 and 1048576 bytes., Photo content type is not included in the list
So how do I tell rspec to upload a photo when it runs my test?
I'm guessing that it's got somethign to do with fixtures.... maybe not though. I've tried playing around with them but not having any luck. For the record, I've created a folder called images inside my fixtures folder and the two files I want to use in my tests are called rails.png and grid.png)
I've tried doing the following:
it "should be valid" do
image = Image.new :photo => fixture_file_upload('images/rails.png', 'image/png').should be_valid
# I've also tried adding stuff like this
#image.stub!(:has_attached_file).with(:photo).and_return( true )
#image.stub!(:save_attached_files).and_return true
#image.save.should be_true
end
But rspec complains about "fixture_file_upload" not being recognised... I am planning to get that Rspec book. And I've trawled around the net for an answer but can't seem to find anything. My test database DOES get populated with some data when I remove the validations from my model so I know that some of it works ok.
Thanks in advance,
EDIT:
images.yml looks like this:
one:
name: MyString
description: MyString
two:
name: MyString
description: MyString
This should work with Rails 2.X:
Image.new :photo => File.new(RAILS_ROOT + '/spec/fixtures/images/rails.png')
As of Rails 3, RAILS_ROOT is no longer used, instead you should use Rails.root.
This should work with Rails 3:
Image.new :photo => File.new(Rails.root + 'spec/fixtures/images/rails.png')
Definitely get the RSpec book, it's fantastic.
Rails.root is a pathname object so you can use it like this:
Image.new :photo => Rails.root.join("spec/fixtures/images/rails.png").open
Edit - probably does not work in Rails 3...
see answer by #Paul Rosania
In case anyone else finds this via Google, RAILS_ROOT is no longer valid in Rails 3.0. That line should read:
Image.new :photo => File.new(Rails.root + 'spec/fixtures/images/rails.png')
(Note the lack of leading slash!)
I use the multipart_body gem in my integration tests. Its a bit truer to BDD than testing.
http://steve.dynedge.co.uk/2010/09/19/multipart-body-a-gem-for-working-with-multipart-data/
With respect to rspec and paperclip, the has_attached_file :photo directive creates a virtual attribute of sorts i.e. :photo ... when you assign a file or a path to photo, paperclip takes over, stores the file, optionally does processing on it e.g. auto-create thumbnails, import a spreadsheet, etc. You aren't telling rspec to test paperclip. You are invoking code and telling rspec what the results of that code -should- be.
In $GEM_HOME/gems/paperclip-2.3.8/README.rdoc, about 76% of the way through the file under ==Post Processing (specifically lines 147 and 148):
---[ BEGIN QUOTE ]---
NOTE: Because processors operate by turning the original attachment into the styles, no processors will be run if there are no styles defined.
---[ END QUOTE ]---
Reading the code, you'll see support :original ... does your has_attached_file define a style?
I use a generic ":styles => { :original => { :this_key_and => :this_value_do_not_do_anything_unless_a_lib_paperclip_processors__foo_dot_rb__does_something_with_them } }" ... just to get paperclip to move the file from some temp directory into my has_attached_file :path
One would think that would be default or more obvious in the docs.

Resources