Testing if a file uploads sucessfully in Rails - ruby-on-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

Related

Ruby on Rails Fixtures in sub-directories

I'm using Rails unit testing and fixtures framework. Unless there's a configuration I haven't seen, I can't use the "Advanced Fixtures" (no id objects) from fixtures in sub-directories:
fixtures/people.yml
_fixture:
model_class: Person
myself:
first_name: Me
last_name: Myself
The following call will pass as expected:
fixtures :people
assert(people(:myself)))
while this one will not (after I move people.yml to subdir):
fixtures "subdir/people"
assert(people(:myself)))
In the later case, the error I get is this:
NoMethodError: undefined method `people'
Using Advanced Fixtures seem valuable but having all of my fixture files in the root of /fixtures seems missing something. I have a few test files and I'd like to have various tests use different fixtures directories.
Any input will be appreciated.
I would try saving people.yml in /fixtures/subdir/people.yml. The documentation is here https://apidock.com/rails/ActiveRecord/TestFixtures/ClassMethods/set_fixture_class .
test_helper.rb
set_fixture_class :people => 'Subdir::People'
in your test file.
before do
myself = subdir_people(:myself)
register(myself)
end
it "should test myself" do
assert(people(:myself)))
end
Here is my final solution:
In the fixture file, I kept:
_fixture:
model_class: Person
In the test file, I got:
def people(sym)
subdir_people(sym)
end
def test_myself
assert(people(:myself))
end
That way, it saves me from refactoring my numerous people() calls. Or I can use subdir_people() if I want.

Creating multiple csv-files and download all in one zip-archive using rails

I am looking for a way to create multiple csv files and download them as one zip archive within one request in my rails application.
To build the archive I use rubyzip gem - to download it just the rails built-in function send_data. The problem I have is that rubyzip's add-function requires a pathname to load files from. But there is no path as my csv files are created within the same request.
Some Code:
# controller action to download zip
def download_zip
zip = #company.download_all
send_data zip, filename: "abc.zip", type: 'application/zip'
end
# method to create zip
def download_all
Zip::File.open('def.zip', Zip::File::CREATE) do |zipfile|
self.users.each do |user|
#some magic to combine zipfile.add() and user.to_csv
end
end
end
# method to create csv
def to_csv
CSV.generate do |csv|
#build awesome csv
end
end
Is there a way to save my csv files temporarely at some directory, that I can pass a pathname to zipfile.add()?
Nice weekend everybody and happy coding!
You could either write your CSV output into a temporary file and call zipfile.add() on that, but there is a cleaner solution:
zipfile.get_output_stream("#{user.name}.csv") { |f| f.puts(user.to_csv) }
See http://rdoc.info/github/rubyzip/rubyzip/master/Zip/File#get_output_stream-instance_method for more details on get_output_stream - you can also pass additional parameters to specify attributes for the file to be created.
get_output_stream doesn't work for me. However, the updated method Zip::OutputStream.write_buffer helps
https://gist.github.com/aquajach/7fde54aa9bc1ac03740feb154e53eb7d
The example adds password protection to the file as well.

How to use binary data in Rails fixtures?

Assume you want to test your brand new Picture model. Probably you want to test whether or not your automatic conversions are working. How do you feed the test data into your fixtures?
To achieve this create a file file_fixtures_extension.rb in your app's folder under config/initializers containing the following code:
require 'active_record/fixtures'
module FileFixtureExtension
def file(file_name)
File::open(Rails.root.join('test/fixtures/', file_name), 'rb') do |f|
"!!binary \"#{Base64.strict_encode64(f.read)}\""
end
end
end
Fixture.extend FileFixtureExtension
Now, you can include binary data from the file test/fixtures/pictures/my-birthday.jpg in your fixtures like this:
first_picture:
name: My Birthday
filename: my-birthday.jpg
content_type: image/jpeg
file: <%=Fixture::file 'pictures/my-birthday.jpg' %>
For further reading on binary data in YAML take a look at the YAML documentation.

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.

How do I test a file upload in 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.

Resources