سؤال

I have a simple test to check that a carrierwave uploader works.

I use minitest for this, and the test works when run on its own, but not under Rails’s rake test... environment.

(Code for the test is included below.)

Things that work:

  • If I run ruby test/uploaders/image_file_uploader_test.rb the test passes.
  • If I run testrb test/uploaders/image_file_uploader_test.rb the test passes.
  • If I manually call all the lines in the test from IRB (not the Rails console), the code does what’s expected
  • If I create a small Rake TestTask to run the file (task also copied below), the test passes.

Things that do not work:

  • If I call rake test test/uploaders/image_file_uploader_test.rb I get dropped into the debugger (stack trace below)
  • If I call zeus rake test test/uploaders/image_file_uploader_test.rb I also get dropped into the debugger

What is strange:

If I head up the call stack in the debugger to the line in the test which has caused the failure – uploader.store!(@file) – and call it directly with (rdb:1) p uploader.store!(@file), it works! By which I mean, the method returns as expected and the file appears in the correct directory.

Thoughts?

I may be doing something really dumb here. It must be something to do with the Rake task loading the Rails environment, right? Am I doubly-including things or something? Is it something to do with the initializer in the Rails environment? (N.B., when I execute store! from the debugger, it stores the file in the location specified in the test, so it is overridding the Rails initializer successfully).

The backtraces

From bundle exec rake test test/uploaders/*_test.rb

# Running tests:

/Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:75: `' (NilClass)
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:926:in `_run_suite'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `block in _run_suites'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `map'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `_run_suites'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:877:in `_run_anything'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1085:in `run_tests'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1072:in `block in _run'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1071:in `each'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1071:in `_run'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1059:in `run'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:795:in `block in autorun'
/Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:75:
(rdb:1) where
--> #1 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:75:in `rmdir'
    #2 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:61:in `with_callbacks'
    #3 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:58:in `store!'
    #4 /Users/leo/Projects/PortfolioSite/test/uploaders/image_file_uploader_test.rb:38:in `test_upload_of_file'
    #5 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1258:in `run'
    #6 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:933:in `_run_suite'
    #7 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `_run_suites'
    #8 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:877:in `_run_anything'
    #9 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1085:in `run_tests'
    #10 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1072:in `_run'
    #11 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1059:in `run'
(rdb:1) up 3
#4 /Users/leo/Projects/PortfolioSite/test/uploaders/image_file_uploader_test.rb:38:in `test_upload_of_file'
(rdb:1) list
[33, 42] in /Users/leo/Projects/PortfolioSite/test/uploaders/image_file_uploader_test.rb
   33    end
   34
   35    # The whole point of this is to upload a file.
   36    def test_upload_of_file
   37      uploader = ImageFileUploader.new
=> 38      uploader.store!(@file)
   39      # Ensure the uploaded file is correct.
   40      assert_equal Digest::SHA2.file(@file).hexdigest, Digest::SHA2.file("#{STORE_PATH}/#{FILENAME}").hexdigest
   41    end
   42
(rdb:1) p uploader.store!(@file)
[:store_versions!]
(rdb:1)

After the call to p uploader.store!(@file), the file has been saved

From zeus test test/uploaders/image_file_uploader_test.rb (this is pretty similar to the above)

/Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:75: `' (NilClass)
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:62:in `loop'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:62:in `go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:78:in `block (3 levels) in go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:78:in `fork'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:78:in `block (2 levels) in go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:73:in `each'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:73:in `block in go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:62:in `loop'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:62:in `go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:78:in `block (3 levels) in go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:78:in `fork'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:78:in `block (2 levels) in go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:73:in `each'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:73:in `block in go'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:62:in `loop'
    from /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:62:in `go'
    from -e:1:in `<main>'
/Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:75:
(rdb:1) where
--> #1 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:75:in `rmdir'
    #2 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:61:in `with_callbacks'
    #3 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/carrierwave-0.9.0/lib/carrierwave/uploader/store.rb:58:in `store!'
    #4 /Users/leo/Projects/PortfolioSite/test/uploaders/image_file_uploader_test.rb:38:in `test_upload_of_file'
    #5 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1258:in `run'
    #6 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:933:in `_run_suite'
    #7 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/parallel_each.rb:71:in `_run_suites'
    #8 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:877:in `_run_anything'
    #9 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1085:in `run_tests'
    #10 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1072:in `_run'
    #11 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/minitest-4.7.5/lib/minitest/unit.rb:1059:in `run'
    #12 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus/m.rb:203:in `execute'
    #13 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus/m.rb:121:in `run'
    #14 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus/m.rb:106:in `run'
    #15 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus/rails.rb:190:in `test'
    #16 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:116:in `command'
    #17 /Users/leo/.rvm/gems/ruby-2.0.0-p195@portfolio_site/gems/zeus-0.13.3/lib/zeus.rb:80:in `go'
(rdb:1) up 3
#4 /Users/leo/Projects/PortfolioSite/test/uploaders/image_file_uploader_test.rb:38:in `test_upload_of_file'
(rdb:1) list
[33, 42] in /Users/leo/Projects/PortfolioSite/test/uploaders/image_file_uploader_test.rb
   33    end
   34
   35    # The whole point of this is to upload a file.
   36    def test_upload_of_file
   37      uploader = ImageFileUploader.new
=> 38      uploader.store!(@file)
   39      # Ensure the uploaded file is correct.
   40      assert_equal Digest::SHA2.file(@file).hexdigest, Digest::SHA2.file("#{STORE_PATH}/#{FILENAME}").hexdigest
   41    end
   42
(rdb:1) p uploader.store!(@file)
[:store_versions!]
(rdb:1)

The code

Class being tested

class ImageFileUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick

  version :thumbnail do
    process resize_to_fill: [100,100]
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

The test itself

require 'minitest/autorun'
require 'minitest/pride'
require 'minitest/debugger' if ENV['DEBUG']
require 'rmagick'
require 'carrierwave'
require_relative '../../app/uploaders/image_file_uploader'

class ImageFileUploaderTest < MiniTest::Unit::TestCase

  # Before any tests run, set up parameters.
  FILENAME = 'test_photo_1.jpg'
  STORE_DIR = 'tmp/uploads/store'
  CACHE_DIR = 'tmp/uploads/cache'
  STORE_PATH = File.join __dir__, '..', '..', STORE_DIR
  CACHE_PATH = File.join __dir__, '..', '..', CACHE_DIR

  # Override the store and cache dirs so we’re not reliant on Rails.
  class ::ImageFileUploader
    storage :file
    store_dir STORE_PATH
    cache_dir CACHE_PATH
  end

  # Before each test runs, set up a file to test with.
  def setup
    @file = File.new "#{__dir__}/../test_files/#{FILENAME}"
  end

  # After each test runs, clear the results directory so it doesn't influence other tests.
  def teardown
    FileUtils.rm_rf STORE_PATH
    FileUtils.rm_rf CACHE_PATH
  end

  # The whole point of this is to upload a file.
  def test_upload_of_file
    uploader = ImageFileUploader.new
    uploader.store!(@file)
    # Ensure the uploaded file is correct.
    assert_equal Digest::SHA2.file(@file).hexdigest, Digest::SHA2.file("#{STORE_PATH}/#{FILENAME}").hexdigest
  end

end

Simple Rake task

require 'rake/testtask'

Rake::TestTask.new('dev:test') do |t|
  t.test_files = FileList['test/uploaders/*_test.rb']
end

Rails initializer (N.B. I think the test is isolated from this...)

CarrierWave.configure do |config|
  config.storage = :file

  # Override the directory where uploaded files will be stored.
  config.store_dir = -> do
    if model.nil?
      "uploads/other/#{Time.now.strftime("%F")}/#{Time.now.strftime("%H-%M-%S")}"
    else
      # This is a sensible default for uploaders that are meant to be mounted:
      "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    end
  end

  # Override the directory where temp files will be stored.
  config.cache_dir = -> do
    # This is a better default because it prevents temp files from becoming public and is more consistent with the Rails directory structure.
    Rails.root.join('tmp/uploads')
  end
end

Thanks!

هل كانت مفيدة؟

المحلول

I have fixed this, although not through an entirely scientific process as I was changing other things in the code, including the overall Rails environment, as I went along.

The current, working version greatly simplifies the Carrierwave initializer, moving path definition to the uploader, and then monkey-patching those methods in the test.

Annoyingly, the original reason for the initializer-based approach was that custom directories were being picked up for the original image, but not for processed versions. That seems to be working fine now, but how this current code differs from the old one I’m not sure.

Rails initializer

CarrierWave.configure do |config|
  config.storage = :file
end

Uploader

class ImageFileUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick

  version :thumbnail do
    process resize_to_fill: [100,100]
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

  def store_dir
    if model.nil?
      "uploads/other/#{Time.now.strftime("%F")}/#{Time.now.strftime("%H-%M-%S")}"
    else
      # This is a sensible default for uploaders that are meant to be mounted:
      "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    end
  end

  def cache_dir
    # This is a better default because it prevents temp files from becoming public and is more consistent with the Rails directory structure.
    Rails.root.join('tmp/uploads')
  end

end

Test

require_relative '../test_helper'
require 'rmagick'
require 'carrierwave'
require_relative '../../app/uploaders/image_file_uploader'

class ImageFileUploaderTest < MiniTest::Unit::TestCase

  # Before any tests run, set up parameters.
  FILENAME = 'test_photo_1.jpg'
  STORE_DIR = 'tmp/uploads/store'
  CACHE_DIR = 'tmp/uploads/cache'
  STORE_PATH = File.join __dir__, '..', '..', STORE_DIR
  CACHE_PATH = File.join __dir__, '..', '..', CACHE_DIR

  # Override the store and cache dirs so we’re not reliant on Rails.
  class ::ImageFileUploader
    storage :file
    def store_dir; STORE_PATH; end
    def cache_dir; CACHE_PATH; end
  end

  # Before each test runs, set up a file to test with.
  def setup
    @file = File.new "#{__dir__}/../test_files/#{FILENAME}"
  end

  # After each test runs, clear the results directory so it doesn't influence other tests.
  def teardown
    FileUtils.rm_rf STORE_PATH
    FileUtils.rm_rf CACHE_PATH
  end

  # The whole point of this is to upload a file.
  def test_upload_of_file
    uploader = ImageFileUploader.new
    uploader.store!(@file)
    # Ensure the uploaded file is correct.
    assert_equal Digest::SHA2.file(@file).hexdigest, Digest::SHA2.file("#{STORE_PATH}/#{FILENAME}").hexdigest
  end

  # After each test, there is nothing to clean up, as teardown clears the output dir.
  def after_tests
  end

end

(N.B. The inclusion of test_helper is not relevant to the fix – after I had it working I moved some of the requires up to the helper to DRY up some overlap with other tests.)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top