How can I speed up Rails unit tests involving image upload/resizing? - ruby-on-rails

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)

Related

Paperclip 4, Amazon S3 & rspec: how to stub file upload?

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.

Where to cleanup cloudinary file uploads after rspec/cucumber run

I use fixture_file_upload in my FactoryGirl methods to test file uploads. Problem is that after cleaning the database, all these uploaded files remain on Cloudinary.
I've been using Cloudinary::Api.delete_resources using a rake task to get rid of them, but I'd rather immediately clean them up before DatabaseCleaner removes all related public id's.
Where should I interfere with DatabaseCleaner as to remove these files from Cloudinary?
Based on #phoet's input, and given the fact that cloudinary limits the amount of API calls you can do on a single day, as well as the amount of images you can cleanup in a single call, I created a class
class CleanupCloudinary
##public_ids = []
def self.add_public_ids
Attachinary::File.all.each do |image|
##public_ids << image.public_id
clean if ##public_ids.count == 100
end
end
def self.clean
Cloudinary::Api.delete_resources(##public_ids) if ##public_ids.count > 0
##public_ids = []
end
end
which I use as follows: in my factory girl file, I make a call to immediately add any public_ids after creating an advertisement:
after(:build, :create) do
CleanupCloudinary.add_public_ids
end
in env.rb, I added
at_exit do
CleanupCloudinary.clean
end
as well as in spec_helper.rb
config.after(:suite) do
CleanupCloudinary.clean
end
This results in, during testing, cleanup after each 100 cloudinary images, and after testing, to clean up the remainder
i would have two ways of doing things here.
firstly, i would not upload anything to cloudinary unless it is a integration test. i would use a mock, stub or test-double.
secondly, if you really really really need to upload the files for whatever reason, i would write a hook that does automatic cleanup in an after_all hook of you tests.
To make #Danny solution work in Minitest, instead of at_exit and config.after, add in test_helper.rb:
class ActiveSupport::TestCase
...
Minitest.after_run do
puts 'Cloudinary cleanup'
CleanupCloudinary.clean
end
end
If you need to cleanup more often, you can use teardown { CleanupCloudinary.clean } either globally in test_helper.rb or in specific test files.
And of course in the factory you still need:
after(:create) do
CleanupCloudinary.add_public_ids
end

How to organize minitest/unit tests?

After using RSpec for several projects, I'm giving minitest/unit a go. I'm liking it so far, but I miss using describe/context blocks to group my tests/specs in a logical way.
I know minitest/spec provides this functionality, but I like that minitest/unit feels a bit closer to barebones Ruby.
Are there any gems that provide describe/context support for minitest/unit? Or, should I just live with my long, unorganized test files in minitest/unit?
I know several folks coming from RSpec to minitest struggling with the same question. They love the ability to nest using describe/context blocks and want to continue in minitest. There are several solutions:
Use minitest's spec DSL: While there are minor differences, the spec DSL gives you most (all?) of the good parts of the rspec DSL. The big difference is the lack of context blocks. But you can just as easily use describe in its place and everything works as you'd expect.
Use directories and files: I prefer this option. I dislike scrolling through a 300 line test file, regardless whether its using the spec DSL or the classical xUnit style. I do not find nesting unrelated tests helpful. The same rules for comprehension for code applies to tests. So break it up. Create a directory and place several files within it.
Here is an example of how my test files are organized:
test/
models/
user/
authentication_test.rb
email_test.rb
reservation_test.rb
user_test.rb
username_test.rb
I use this structure whether I'm using the spec DSL or the xUnit style. When using the spec DSL I specify what I'm testing in my describe block like so:
require "minitest_helper"
describe User, :authentications do
before do
# ...
You can also throw multiple classes into one test file:
module PizzaTest
class Isolation < ActiveSupport::TestCase
test "is awesome by default" do
assert Pizza.new.awesome?
end
end
class Integration < ActiveSupport::TestCase
fixtures :all
test "is awesome too" do
pizzas('one-with-everything').awesome?
end
end
end
and even nest test classes:
class PizzaTest < ActiveSupport::TestCase
test "is awesome by default" do
assert Pizza.new.awesome?
end
class Integration < ActiveSupport::TestCase
fixtures :all
test "is awesome too" do
assert pizzas('one-with-everything').awesome?
end
end
end
I prefer this way (only a little bit) but I think it easier to follow:
class ConventionalNameTest < ActiveSupport::TestCase
class ContextTest < ConventionalNameTest
# so much stuff...
end
class AnotherContextTest < ConventionalNameTest
# and some more...
end

Paperclip/Rspec tests: Is there a faster way to test paperclip validates_attachment_content_type?

One thing I've noticed is that in most of the projects I do, the one spec that always takes a long time (30 seconds +) is this shoulda/paperclip helper:
it { should validate_attachment_content_type(:bannerimage)
.allowing('image/png', 'image/jpeg', 'image/gif', 'image/jpg')
.rejecting('text/plain')
}
I'd quite like to keep content type validation in, but I'm wondering if there's a speedier way to do it. I already tag these tests with a :slow and run rspec without :slow specs, but nonetheless, I'm hoping someone has a swifter way of testing image content types.
Looks like you're running your own tests against paperclip.
Generally I let gem providers (especially for large products like this one) certify that their specs will run successfully before pushing a release.
I stub out actual paperclip stuff from my tests to make them faster like this, placed in spec_helper.rb
# stub out paperclip? http://pivotallabs.com/stubbing-out-paperclip-imagemagick-in-tests/
# only like .1 seconds faster anyways though...
module Paperclip
def self.run cmd, params = "", expected_outcodes = 0
case cmd
when "identify"
return "100x100"
when "convert"
return
else
super
end
end
end
class Paperclip::Attachment
def post_process
end
end

How to create unit test for Paperclip::Geometry?

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

Resources