Search code examples
ruby-on-railsruby-on-rails-5carrierwaverspec-railsrspec3

Rspec rails compare uploaded files md5 hash


I need to check that a file has been changed after an upload (that a placeholder logo was replaced by a real logo, so I cannot just use file.present?)

I have a code that will run a md5 hash on the real file, however I have problems accessing this file from a rspec test.

Where does RSpec place files during tests ? Suppose I have a model with

mount_uploader :logo

In my specs I am trying to do

it 'updates with a real logo' do
  logo_absolute_path = File.join(Rails.root, model.logo.url)
  expect { request }.to change_file(clean_path)
end

But File.join(Rails.root, model.logo.url) raises the following

No such file or directory @ rb_sysopen - /Users/Me/dev/example/uploads/model/5ad86444aba9cf23416700f0/logo.png

So I'm guessing this is not where file are stored in test mode. Anyone could tell me how I can generate the local absolute file path to test the content in test environment ?

Note : expect { request }.to change_file(clean_path) is a matcher based on https://gist.github.com/mattwynne/736421

EDIT the code for my matcher as it may matter :

RSpec::Matchers.define(:change_file) do |file_path|
  supports_block_expectations
  match do |procedure|
    before_hash = md5_hash(file_path)
    procedure.call
    after_hash = md5_hash(file_path)
    expect(before_hash).not_to eq(after_hash)
  end

  def md5_hash(file_path)
    Digest::MD5.hexdigest(File.read(file_path))
  end
end

Solution

  • To get the path in the test, after creating a new instance of Object, I am calling:

    Object.mounted_as.file.path
    

    So in your case it would be:

    it 'updates with a real logo' do
      logo_absolute_path = model.logo.file.path      
      expect { request }.to change_file(clean_path)
    end
    

    Also, please make sure that you have set logo for the model (for instance, with FactoryBot).

    Where carrierwave stores files?

    In my case, the uploader class the following store_dir method:

    def store_dir
      "#{UPLOADS_PATH}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    end
    

    Where UPLOADS_PATH is:

    UPLOADS_PATH = "#{Rails.root.to_s}/private/uploads/#{Rails.env}"
    

    So, in my case, files for tests are placed under:

    /APP_ROOT/private/uploads/test/MODEL/MOUNDED_AS/OBJECT_ID/FILE_NAME