18

I'm working on upgrading from attachment_fu to carrierwave, since attachment_fu is broken in rails 3.

None of the tests are able to run, because we have invalid fixtures that were using the syntax from attachment_fu for attachment files.

For example, we have a Post model that has one PostAttachment. Here's what the data in the PostAttachment fixture looks like:

a_image:
  post_id: 1
  attachment_file: <%= Rails.root>/test/files/test.png

And this is the error I'm getting:

ActiveRecord::StatementInvalid: PGError: ERROR:  column "attachment_file" of relation "post_attachments" does not exist
LINE 1: INSERT INTO "post_attachments" ("post_id", "attachment_file"...

attachment_file would have been picked up by attachment_fu, and it would have taken care of all the processing to create the attachment_fu attachment for the model.

Is there a way to have image attachments in the fixtures, but with using CarrierWave instead?

keithepley
  • 4,760
  • 3
  • 23
  • 41

6 Answers6

20

The only way I've managed to get this to work is to use a storage provider specifically for testing that doesn't actually save/read files.

In your config/initializers/carrier_wave.rb Add a NullStorage class that implements the minimum interface for a storage provider.

# NullStorage provider for CarrierWave for use in tests.  Doesn't actually
# upload or store files but allows test to pass as if files were stored and
# the use of fixtures.
class NullStorage
  attr_reader :uploader

  def initialize(uploader)
    @uploader = uploader
  end

  def identifier
    uploader.filename
  end

  def store!(_file)
    true
  end

  def retrieve!(_identifier)
    true
  end
end

Then when initializing CarrierWave add a clause for the test environment, e.g.,

if Rails.env.test?
    config.storage NullStorage
end

Here is a gist of my complete carrier_wave.rb for reference. It also includes how to setup S3 for uploads in staging/production and local storage for development so you can see how to configure CarrierWave in context.

Once CarrierWave is configured you can simply put any string in the fixtures column to simulate an uploaded file.

Gerry Shaw
  • 9,178
  • 5
  • 41
  • 45
  • When I tried with a file arg in the factory, I got a `ArgumentError: is not a recognized storage provider` error. Converting to a string, as you suggest, makes it work! Yay! Thanks! – brookr Feb 21 '15 at 08:31
  • 2
    Well, I was hopeful, but... This is still showing as 'not a recognized storage provider' when I visit a page with a form field in my feature specs. Is there some way to register the NullStorage as a recognized provider?? – brookr Feb 21 '15 at 08:48
  • I'm not completely sure what NullStorage does; but I have unit tests that do things like email out attachments of previously uploaded files - how does one test those? I tried the above steps and they don't seem to help. Thanks. – Gerry Feb 23 '16 at 21:57
  • I have a set up with fog — I get an error: `bucket_name is required` – fatuhoku Jun 20 '16 at 15:56
  • How is this meant to work when you validate the presence of the uploaded file? If `NullStorage` doesn't actually store your file... – henrebotha Mar 15 '17 at 14:16
  • If your tests have to actually work with uploaded files this isn't going to work for you. It's used when your tests can just assume uploading works. Note that I've since moved to Shrine for uploading in Rails projects so take this answer with a grain of salt now. – Gerry Shaw Mar 15 '17 at 22:04
  • I ran into some problems with tests that should remove files. The error was `undefined method `delete' for true:TrueClass` I figured that the `retrieve!` shouldn't return `true` but rather `nil` - as that's even in the class name. :) It should "write anything" & "don't read anything" :) – PL J May 12 '17 at 11:21
9

Try passing a file instead of a String.

a_image:
    post_id: 1
    attachment_file: File.open(Rails.root.join("test/files/test.png"))

This works for me using FactoryGirl

Note: Edit thanks to @dkobozev

e3matheus
  • 2,112
  • 1
  • 20
  • 28
  • 4
    `File.open(Rails.root + "/test/files/test.png")` does not work for me. `File.open(Rails.root.join("test/files/test.png"))` does. – dkobozev Oct 19 '11 at 23:26
  • 1
    On current versions of carrierwave this isn’t working for me. I’ve tried quoting/escaping/ERBing the above `File.open...` call. I’ve also tried using `Rack::Test::UploadedFile.new(Rails.root.join("test/files/test.png"))`, which works when it’s passed as a parameter. – Leo Oct 23 '13 at 12:12
  • 3
    This doesn't work for fixtures. If nothing else you would have to escape the Ruby code with ERB tags but even then it doesn't work. – Gerry Shaw Aug 14 '14 at 19:13
  • 2
    This didn't work for me either in Rails 4.1, What I ended up doing was assigning the files to their respective attributes inside the test. Not sure what's wrong with this method, but passes... – daveomcd Jan 15 '15 at 16:53
  • 1
    This worked for me, but in order to use it with fixtures I needed to add `attachment_file: <%= File.open(Rails.root.join("test/files/test.png")) %>` – joseramonc May 15 '15 at 03:29
2

config/initializers/carrier_wave.rb

In Rails 4

# class NullStorage is defined here before the following block

if Rails.env.test?
  CarrierWave.configure do |config|
    config.storage NullStorage
  end
end

& in fixtures:

a_image:
  post_id: 1
  attachment_file: <%= File.open(Rails.root.join("test/files/test.png")) %>
Serjik
  • 10,543
  • 8
  • 61
  • 70
1

To be able to use fixtures that have uploaded files as well as doing uploads in the tests, I've played around with CarrierWave for a bit lately. I've written an article about how I'd do it.

jkreeftmeijer
  • 105
  • 2
  • 9
0

I know it is old but, for some that uses Rails 5 + RSpec + CarrierWave + Fixtures:

Edit test configs:

# config/initializers/carrierwave.rb
if Rails.env.test?
  class NullStorage < CarrierWave::Storage::Abstract
    def store!(_file)
      _file
    end

    def retrieve!(identifier)
      file = Rails.root.join('spec', 'fixtures', 'files', identifier)
      tmp = Rails.root.join('tmp', 'blank_tmp.jpg')
      FileUtils.cp(file, tmp)
      CarrierWave::SanitizedFile.new(tmp)
    end
  end

  CarrierWave.configure do |config|
    config.storage = NullStorage
    config.enable_processing = false
  end
end

Create a folder and a file, for example spec/fixtures/files/some-user-photo.jpg

and, create some fixtures, for example:

first_user:
  avatar: "some-user-photo.jpg"
  name: "First User Name"
  about: "First User About Long Text..."
  lat: 0.001
  lng: 0.001
  created_at: <%= Time.current - 3.days %>
  updated_at: <%= Time.current - 3.days %>

That is enough to make the test understand that this user has an avatar

alvesoaj
  • 512
  • 5
  • 16
-4

We have just removed the fixtures all together, the system seeds this files for each test. Ask yourself... do you need al these fixtures here for this test? No probably not. And Fixtures dont BANG! so we just use Model.create!( ... ) with specific data for the test

Rene Weteling
  • 524
  • 3
  • 8