madcow madcow - 3 months ago 38
Ruby Question

Stubbing Paperclip downloads from S3 in RSpec

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?

Answer

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