Krzychu
Krzychu

Reputation: 241

Rails carrierwave testing - how to remove file after test?

I am testing the carrierwave upload functionality using rspec and capybara. I have something like:

describe "attachment" do
    let(:local_path)  { "my/file/path" }
    before do
      attach_file('Attachment file', local_path)
      click_button "Save changes"       
    end

    specify {user.attachment.should_not be_nil}
    it { should have_link('attachment', href: user.attachment_url) }
end

And this works great. The problem is that after testing the uploaded image remains in my public/uploads directory. How can I remove it after the test is done? I tried something like this:

after do
   user.remove_attachment!
end

but it did not work.

Upvotes: 1

Views: 4198

Answers (4)

Marcis Viskints
Marcis Viskints

Reputation: 481

A cleaner solution that seems to work for me is the following in spec/support/carrierwave.rb:

uploads_test_path = Rails.root.join('uploads_test')

CarrierWave.configure do |config|
  config.root = uploads_test_path
end

RSpec.configure do |config|
  config.after(:suite) do
    FileUtils.rm_rf(Dir[uploads_test_path])
  end
end

This would set the whole root folder specific to the test environment and delete it all after the suite, so you do not have to worry about store_dir and cache_dir separately.

Upvotes: 4

Todd
Todd

Reputation: 3103

The latest CarrierWave documentation for this technique is as follows:

config.after(:suite) do
  if Rails.env.test? 
    FileUtils.rm_rf(Dir["#{Rails.root}/spec/support/uploads"])
  end 
end

Note that the above simply assumes youre using spec/support/uploads/ for images and you dont mind deleting everything in that directory. If you have different locations for each uploader, you may want to derive the upload and cache directories straight from the (factory) model:

config.after(:suite) do
  # Get rid of the linked images
  if Rails.env.test? || Rails.env.cucumber?
    tmp = Factory(:brand)
    store_path = File.dirname(File.dirname(tmp.logo.url))
    temp_path = tmp.logo.cache_dir
    FileUtils.rm_rf(Dir["#{Rails.root}/public/#{store_path}/[^.]*"])
    FileUtils.rm_rf(Dir["#{temp_path}/[^.]*"])
  end
end

or, if you want to delete everything under the CarrierWave root that you set in an initializer, you can do this:

config.after(:suite) do
  # Get rid of the linked images
  if Rails.env.test? || Rails.env.cucumber?
    FileUtils.rm_rf(CarrierWave::Uploader::Base.root)
  end
end

Upvotes: 0

Steve Madere
Steve Madere

Reputation: 509

Ha! I found the answer to this today.

The auto-removal of the downloaded file is done in an after_commit hook. These do not get run by default in rails tests. I never would have guessed that.

It is however documented offhandedly in a postscript note here: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit

I discovered this by deep diving into the carrierwave code with a debugger and just happened to notice it in the comments above the source code to after_commit when I stepped into it.

Thank goodness ruby libraries are not stripped of comments at runtime like JS. ;)

The workaround suggested in the docs is to include the 'test_after_commit' gem in your Gemfile BUT ONLY IN THE TEST ENVIRONMENT.

i.e.

Gemfile:

...
gem 'test_after_commit', :group => :test
...

When I did this, it completely solved the problem for me.

Now, my post-destruction assertions of cleanup pass.

Upvotes: 0

apneadiving
apneadiving

Reputation: 115531

You're not the only one having issues to delete the files in carrierwave.

I ended up doing:

user.remove_attachment = true
user.save

I got this tip reading this.

Upvotes: 5

Related Questions