Search code examples
ruby-on-railsrubyrspecpapercliprspec-rails

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?


Solution

  • 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