Paperclip 4, Amazon S3 & rspec: how to stub file upload? - ruby-on-rails

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.

Related

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 do I generate website thumbnails without 3rd party tools?

Are there any gems out there for Rails 3.2.1 that generate website thumbnails? I see a lot of 3rd party solutions but I don't like the fact that they aren't hosted on my server. It's really important the app I'm building is as stable as possible and I think this is not a good solution in the long run.
My ruby knowledge is fairly good, I think enough to use a gem and implement it, but definitely not good enough to write something like this from scratch if no gems exist.
Thanks!
You could try dragonfly or carrierwave
Well, here's the first thing that came up on Rubygems: thumbnailer. It uses Amazon and costs a small fee per image it generates, so you probably don't want this...
But there's also thumbnailer-ruby which looks like it works completely on the local machine. Haven't tested it out, though. It appears that this doesn't actually do what you want. Nevermind.
Now another gem called snapurl looks pretty fancy. Once again, I haven't tried it out yet. I'll do that now.
EDIT: Won't run for me; keeps failing with an error.
https://url2png.com/ has worked great so far
aBrowshot has a gem available.
There's no need to use a third party service for this.
You can do something like this in your model:
class MySexyModel < ActiveRecord::Base
... stuff
# Generate the thumbnail on validate so we can return errors on failure
validate :generate_thumbnail_from_url
# Cleanup temp files when we are done
after_save :cleanup_temp_thumbnail
# Generate a thumbnail from the remote URL
def generate_thumbnail_from_url
# Skip thumbnail generation if:
# a) there are already other validation errors
# b) an image was manually specified
# c) an image is already stored and the URL hasn't changed
skip_generate = self.errors.any? || (self.image_changed? ||
(self.image_stored? && !self.url_changed?))
# p "*** generating thumbnail: #{!skip_generate}"
return if skip_generate
# Generate and assign an image or set a validation error
begin
tempfile = temp_thumbnail_path
cmd = "wkhtmltoimage --quality 95 \"#{self.url}\" \"#{tempfile}\""
# p "*** grabbing thumbnail: #{cmd}"
system(cmd) # sometimes returns false even if image was saved
self.image = File.new(tempfile) # will throw if not saved
rescue => e
# p "*** thumbnail error: #{e}"
self.errors.add(:base, "Cannot generate thumbnail. Is your URL valid?")
ensure
end
end
# Return the absolute path to the temporary thumbnail file
def temp_thumbnail_path
File.expand_path("#{self.url.parameterize.slice(0, 20)}.jpg", Dragonfly.app.datastore.root_path)
end
# Cleanup the temporary thumbnail image
def cleanup_temp_thumbnail
File.delete(temp_thumbnail_path) rescue 0
end
end
The original post is on this blog: http://sourcey.com

How to stub carrierwave in Rspec?

I want to stub carrierwave to prevent it from fetch images on the web during my tests. How would I stub things to achieve this?
My crawler parses a remote web page, and saves one image url into the model. Carrierwave will fetch that image automatically during the save operation. It works well.
However I have a test about the parsing of pages, and every-time it will download the file, which slows down the testing.
UPDATE:
I mount the uploader as the following (in the pre-existing paperclip column)
mount_uploader :image, TopicImageUploader, :mount_on => :image_file_name
I tried to stub the following, but neither worked:
Topic.any_instance.stub(:store_image!)
Topic.any_instance.stub(:store_image_file_name!)
Topic.any_instance.stub(:store_image_remote_url!)
TopicImageUploader.any_instance.stub(:download!)
This is what I'm using in my spec_helper:
class CarrierWave::Mount::Mounter
def store!
end
end
This completely blocks all real file uploads (note that I'm using this with carrier wave 0.5.8, which is newest version at the time of writing, if you're using much older version, it might differ). If you want to control tests which stub uploads, you could use:
CarrierWave::Mount::Mounter.any_instance.stub(:store!)
I reduced my test-suite time from 25 seconds to just 2 seconds with a simple config in the CarrierWave initializer:
# config/initializers/carrier_wave.rb
CarrierWave.configure do |config|
config.enable_processing = false if Rails.env.test?
end
This config skips the image manipulation (resizing, cropping, ...) of ImageMagick, MiniMagick ect.
allow_any_instance_of(CarrierWave::Uploader::Base).to receive(:store!).and_return nil

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 can I speed up Rails unit tests involving image upload/resizing?

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)

Resources