-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Using CarrierWave with Minitest and Rails 5
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:
- Extra files are created as your tests run
- 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.