I'm writing unit test for a image uploader that uses paperclip.
It's being bumpy and I was slowly moving thought the hurdles till I got stuck in the paperclip::geometry class
Here is my code below
require 'test_helper'
require File.join(File.dirname(__FILE__),"../../config/initializers","paperclip")
class PhotoTest < ActiveSupport::TestCase
#include ActionController::TestProcess
should_belong_to(:product)
should_have_attached_file :data
setup do
#I had to do this way because the include right below the class line was not working
image = Photo.create(:data => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + "base-production-pack.png",'image/png'))
#geo = Paperclip::Geometry.from_file(image)
end
end
the paperclip::geometry is giving me the error:
test: Photo should have a paperclip attachment named #data. (PhotoTest):
Paperclip::NotIdentifiedByImageMagickError: #<Photo:0x1054aa6b8> is not recognized by the 'identify' command.
paperclip (2.3.6) lib/paperclip/geometry.rb:24:in `from_file'
/test/unit/photo_test.rb:13
I have a initializer file "paperclip.rb" that points to the identify on my local machine
Thanks in advance
Paperclip::Geometry.from_file expects a path, so you should do:
Paperclip::Geometry.from_file(image.data.path)
Sidenote: It's good that you're testing, however there's little point testing paperclip's geometry method because paperclip's test suite covers that. Your tests should cover what your code is expected to do (eg confirming thumbnails are resized as expected).
Related
I have been banging my head with Carrierwave. I finally was able to get carrier wave to upload multiple files by adding the master branch for Carrierwave to my Gemfile:
gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
I used it for a model called Product and I did this.
Product.rb
class Product < ActiveRecord::Base
mount_uploaders :avatar, ProductUploader
end
And I essentially followed the rules and was able to upload multiple files all while having each file iterate through the create method in my products_controller.rb and creating a new Product instance for each file being uploaded.
Now. Here comes the Testing. Before, when only one file was being uploaded I was able to use,
test "should create product" do
login
excel_filename = 'files/product_create_test.xls'
file = fixture_file_upload(excel_filename, 'application/excel')
assert_difference('Product.count') do
post :create, product: {:file_url => file}
end
But now, after adding the ability to upload multiple files, it seems like fixture_file_upload is not working properly.
I am getting this error:
ProductsControllerTest#test_should_create_product:
ArgumentError: wrong number of arguments (2 for 0)
test/controllers/products_controller_test.rb:54:in `block (2 levels) in <class:ProductsControllerTest>'
test/controllers/products_controller_test.rb:53:in `block in <class:ProductsControllerTest>'
I am not sure how to go around this. Like I said, when I had
gem 'carrierwave'
the previous test worked fine. Has anyone ever encountered this?
Here's what the problem was. In order to make it so that my fixture file uploads did not actually save in my test enviroment, I followed this article, http://jeffkreeftmeijer.com/2014/using-test-fixtures-with-carrierwave/#comment_768.
Here it tells you to add the following to your test_helper file
class CarrierWave::Mount::Mounter
def store!
# Not storing uploads in the tests
end
end
This essentially tells your testing environment not to store the uploaded fixtures anywhere. Anyway, this piece of code does not fly well with the addition of the carrier wave master branch for multiple file uploads. I ended up uncommenting it and despite the fact that is stores the uploaded files, my tests pass.
I am using Paperclip/RSpec and StackOverflow has helped me successfully stub file uploads to S3 using this code:
spec/rails_helper.rb
config.before(:each) do
allow_any_instance_of(Paperclip::Attachment).to receive(:save).and_return(true)
end
This is working great.
On my model I have two Paperclip fields:
class MyModel < ActiveRecord::Base
has_attached_file :pdf
has_attached_file :resource
end
My code uses the #copy_to_local_file method (Docs) to retrieve a file from S3.
#copy_to_local_file takes two params: the style (:original, :thumbnail, etc) and the local file path to copy to.
Example:
MyModel.resource.copy_to_local_file(:original, local_file.path)
When the system under test tries to access MyModel#pdf#copy_to_local_file or MyModel#resource#copy_to_local_file, I originally got errors like the following:
No Such Key - cannot copy /email_receipts/pdfs/000/000/001/original/email_receipt.eml.pdf to local file /var/folders/4p/1mm86g0n58x7d9rvpy88_s9h0000gn/T/receipt20150917-4906-13evk95.pdf
No Such Key - cannot copy /email_receipts/resources/000/000/001/original/email_receipt.eml to local file /var/folders/4p/1mm86g0n58x7d9rvpy88_s9h0000gn/T/resource20150917-4906-1ysbwr3.eml
I realize these errors were happening because uploads to S3 are stubbed, so when it encounters MyModel#pdf#copy_to_local_file or MyModel#resource#copy_to_local_file it tries to grab a file in S3 that isn't there.
Current Solution:
I've managed to quash the errors above, but I feel it's not a complete solution and gives my tests a false sense of security. My half-solution is to stub this method in the following way:
spec/rails_helper.rb
before(:each) do
allow_any_instance_of(Paperclip::Storage::S3).to receive(:copy_to_local_file)
end
While this does stub out the #copy_to_local_file method and removes the errors, it doesn't actually write any content to the local file that is provided as the second argument to #copy_to_local_file, so it doesn't quite simulate the file being downloaded from S3.
Question:
Is there a way to stub #copy_to_local_file AND have it write the contents of a canned file in my spec/factories/files directory to the local file (its second argument)?
Or am I overthinking this? Is this something I shouldn't be worrying about?
You don't need to worry about whether the 'downloaded' files actually exist in your tests. You've decided to stub out Paperclip, so do it completely, by stubbing out both #save and #copy_to_file. You may also need to stub out reads of downloaded files from the filesystem.
All this stubbing raises the possibility of integration errors, so you should probably write a feature spec (using a captive browser like poltergeist) that actually uploads and downloads something and reads it from the filesystem.
That said, you can do anything you want in an RSpec stub by passing it a block:
allow_any_instance_of(Paperclip::Storage::S3).to receive(:copy_to_local_file) do |style, local_dest_path|
# write a file here, or do anything you like
end
I'm writing tests with rspec and am a bit struggling with Paperclip 4. At the moment I'm using webmock to stub requests but image processing is slowing tests down.
Everything I read suggest to use stubs like so:
Profile.any_instance.stub(:save_attached_files).and_return(true)
It doesn't work since :save_attached_files disappeared with Paperclip 4 as far as I can tell.
What's the proper way to do it now ?
Thanks
Add this to your rails_helper.rb in your config block:
RSpec.configure do |config|
# Stub saving of files to S3
config.before(:each) do
allow_any_instance_of(Paperclip::Attachment).to receive(:save).and_return(true)
end
end
It doesn't exactly answer my question, but I found a way to run faster specs thanks to this dev blog, so I'm posting it if it can help someone else.
Just add this piece of code at the beginning of your spec file or in a helper. My tests are running 3x faster now.
# We stub some Paperclip methods - so it won't call shell slow commands
# This allows us to speedup paperclip tests 3-5x times.
module Paperclip
def self.run cmd, params = "", expected_outcodes = 0
cmd == 'convert' ? nil : super
end
end
class Paperclip::Attachment
def post_process
end
end
Paperclip > 3.5.2
For newer versions of Paperclip, use the following:
module Paperclip
def self.run cmd, arguments = "", interpolation_values = {}, local_options = {}
cmd == 'convert' ? nil : super
end
end
class Paperclip::Attachment
def post_process
end
end
I had a heckuva time dealing with this. I didn't want to test Paperclip per se -- rather, I needed a file to exist on my model so I can test a very sensitive custom class.
None of the other answers worked for me (Rails 5, Rspec 3.5) so I gave up on stubbing AWS or using the Paperclip::Shoulda::Matchers. Nothing worked and it ate up an entire day! Finally I gave up on Paperclip altogether and went this route:
list = build(:list)
allow(list).to receive_message_chain("file.url") { "#{Rails.root}/spec/fixtures/guess_fullname.csv" }
importer = Importer.new(list)
expect(importer.set_key_mapping).to eq({nil=>:email_address, :company=>:company, :fullname=>:full_name})
Where my models/list.rb has has_attached_file :file and Importer is a class I wrote that takes the file, which was already uploaded to S3 by paperclip before the class is initialized, and processes it. The file.url would otherwise be the S3 URL, so I give it the path in my repo to a testing csv file. set_key_mapping is a method in my class that I can use to verify the processing part worked.
Hope this saves somebody a few hours...
What about adding AWS.stub! in your spec/spec_helper.rb? Give that a try.
I'm trying to test my photo uploads with Fabricator and paperclip, but I'm having trouble using fabricator to create a paperclip object.
My current thought process is to include this module:
http://room118solutions.com/2011/05/25/stubbing-paperclip-during-testing/
After including, I should be able to fabricate on it??
Unfortunately, I don't quite know enough about Fabricator to do this.
If you want just to test something related to upload logic, and your test suite does not include fabricating lot of objects with attachments, you can do this:
Fabricator(:fabricator_name) do
image { File.open(File.join(Rails.root, 'spec', 'fabricators', 'assets', 'image.jpg'))}
end
And place any small image named image.jpg to spec/fabricators/assets/ folder. As noted here in question.
My app does a lot with images. I use paperclip to attach them to models. I have tons of tests (Test::Unit) that involve creating images, these run pretty slowly.
I use FactoryGirl to create models in my tests. This is how I create image attachments:
factory :product_image_100_100 do
image File.new(File.join(::Rails.root.to_s, "/test/fixtures/images", "100_100.jpg"))
end
How can I fake the image upload or otherwise speed things up?
This snippet worked for me:
require 'test_helper'
class PhotoTest < ActiveSupport::TestCase
setup do
Paperclip::Attachment.any_instance.stubs(:post_process).returns(true)
end
# tests...
end
Upd. My current preference is to stub out ImageMagic globally, by adding the following to my test_helper.rb:
module Paperclip
def self.run(cmd, *)
case cmd
when "identify"
return "100x100"
when "convert"
return
else
super
end
end
end
(Adapted from here – btw, you may want to take a look at this article if you're interested in speeding up your tests)