Skip to content

Using CarrierWave with Minitest and Rails 5

vshy108 edited this page Jan 5, 2018 · 2 revisions

I use Minitest with Rails 5 testing, and CarrierWave isn't difficult to set up for testing. You just need to point CarrierWave's root directory to somewhere in your test structure and it'll work. Kind of.

There are two issues that must be overcome:

  1. Extra files are created as your tests run
  2. Files that you put in for fixtures may be removed by a test

It's actually easy to make it work consistently.

First, I removed the CarrierWave configuration for the test environment from the config/initializers/carrierwave.rb file and put it in test/test_helper.rb. That allows me to keep everything in one single place. Next, I set up a "template" directory in test/fixtures/files/uploads that will have the initial CarrierWave files that are referenced from my fixtures.

When tests are run, I copy the "template" structure to a temporary location that will be CarrierWave's root, and when the tests exit the structure is removed. The template is untouched and ready for the next testing round.

I set CarrierWave's root to test/support/carrierwave with carrierwave_cache and uploads as subdirectories. This is the relevant code from test/test_helper.rb:

require 'fileutils'

# Carrierwave setup and teardown
carrierwave_template = Rails.root.join('test','fixtures','files')
carrierwave_root = Rails.root.join('test','support','carrierwave')

# Carrierwave configuration is set here instead of in initializer
CarrierWave.configure do |config|
  config.root = carrierwave_root
  config.enable_processing = false
  config.storage = :file
  config.cache_dir = Rails.root.join('test','support','carrierwave','carrierwave_cache')
end

# And copy carrierwave template in
#puts "Copying\n  #{carrierwave_template.join('uploads').to_s} to\n  #{carrierwave_root.to_s}"
FileUtils.cp_r carrierwave_template.join('uploads'), carrierwave_root

at_exit do
  #puts "Removing carrierwave test directories:"
  Dir.glob(carrierwave_root.join('*')).each do |dir|
    #puts "   #{dir}"
    FileUtils.remove_entry(dir)
  end
end

The other issue is how to handle files that are referenced from your fixtures. You can figure out what they'll be named manually, or just do it the easy way. Add code like this to your model test:

test "show me the full filepath of my picture" do
  puts "\n\n#{users(:mike).picture.file.path}\n\n"
end

Then run your test. Your fixture should simply have the filename in it:

mike:
  name: Mike
  picture: mike.jpg

When you run your test, in the middle of it all you'll see the path:

/Users/mike/proj/public/uploads/user/picture/762146111/mike.jpg

So ignore the path parts before "uploads" and copy the picture file to the given location:

mkdir -p test/fixtures/files/uploads/user/picture/762146111
cp mike.jpg test/fixtures/files/uploads/user/picture/762146111/mike.jpg

With that accomplished, your test data will now have mike.jpg available to the test fixture data. Be aware that if you have a test that removes this record or this picture the file will no longer be available in subsequent tests. I recommend that if you have a test that is destructive of CarrierWave data you create a disposable record.

In order to test file uploads properly, use fixture_file_upload to reference the file. I add a couple of small test pictures to test/fixtures/files to make this easy:

  test "should create user" do
    assert_difference('User.count') do
      post admin_users_url, params: { user: { name: 'Test User', picture: fixture_file_upload(Rails.root.join('test','fixtures','files', 'clear.gif')) } }
    end

    assert_redirected_to admin_user_url(User.last)
  end

I use actual files to test and don't have speed problems (SSDs help here). This should get you started on testing using Minitest and Rails 5 with CarrierWave.

Clone this wiki locally