Testing carrierwave file uploads with RSpec and FactoryGirl

Bài viết được dịch từ blog: https://til.codes/testing-carrierwave-file-uploads-with-rspec-and-factorygirl/

Trong bài viết này tác giả chỉ muốn tập trung vào việc sử dụng Carriewavve trong testing mà cụ thể là Rspec. Vì vậy nếu các bạn muốn biết cách cài đặt hoặc cấu hình carrierwave cho project của mình thì các bạn nên tham khảo trong wiki của carrierwave tại đây

Giả sử mọi thứ đã được cài đặt đầy đủ cho project của bạn, chúng ta có một model Attachment sử dụng Carrierwave uploader(FileUploader) như dười đây:

# app/models/attachment.rb
class Attachment < ActiveRecord::Base  
  mount_uploader :file, FileUploader
end  

Để bắt đầu sử dụng trong testing, chúng ta cần tạo ra các bản ghi Attachment trong testing. Vì vậy chúng ta cần cập nhật factory của Attachment như sau:.

# spec/factories/attachments.rb
FactoryGirl.define do  
 factory :attachment do
   photo Rack::Test::UploadedFile.new(File.open(File.join(Rails.root, '/spec/fixtures/myfiles/myfile.jpg')))
 end
end  

Trong đoạn code trên chúng ta đã đính kèm file trong thuw mục /spec/fixtures/myfiles/ folder vào trường photo. Có một chú ý là đoạn code trên chỉ có tác dụng đính kèm một file vào trường photo khi bạn build một object mới. Nếu bạn sử dụng phương thức create và muốn tạo một bản ghi tồn tại thực sự trong DB thì ta cần cập nhật đường dẫn file vào cột photo như sau:

FactoryGirl.define do  
 factory :attachment do
    after :create do |b|
      b.update_column(:photo, "foo/bar/baz.png")
    end
  end
end

Với tất cả các cài đặt ở trên thì ta đã đủ để sử dụng factory của Attachment trong Rspec như tất cả các model bình thường để testing. Tuy nhiên, sau đây tác giả sẽ trình bày thêm cách tối ưu để thời gian chạy test là nhanh nhất

  1. Cài đặt kiểu storage là file đối với môi trường test. 2.Loại bỏ quá trình xử lý file với môi trường test.
  2. Phân tách thư mục lưu file cho môi trường test khác các môi trường khác.
  3. Xóa file đã upload sua mỗi request.

Cài đặt Carrierwave để sử dụng local storage and disable quá trình xử lý file test env

Để làm được các công việc trên, chúng ta sẽ thêm cấu hình của Carrierwave trong thư mục initializer như sau:

# config/initializers/carrierwave.rb
if Rails.env.test? || Rails.env.cucumber?  
  CarrierWave.configure do |config|
    config.storage = :file
    config.enable_processing = false
  end
end  

Phân tách thư mục chứa file của môi trường test khác với các môi trường khác.

Tiếp theo, chúng ta nên chia thư mục chứa file upload của môi trường test riêng biệt với các môi trường khác (production, development....). Chúng ta có thể sửa các phương thức cache_dirstore_dir cho tất cả các Carrierwave models hoặc ghi đè trong file config của initializers như dưới đây:

# config/initializers/carrierwave.rb
  CarrierWave::Uploader::Base.descendants.each do |klass|
  next if klass.anonymous?
  klass.class_eval do
    def cache_dir
      "#{Rails.root}/spec/support/uploads/tmp"
    end

    def store_dir
      "#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
    end
  end
end

Xóa file sua mỗi request.

Thêm đoạn code sau vào file spec_helper.rb để chắc chắn rằng file sẽ được xóa sau mỗi test:

# spec_helper.rb
RSpec.configure do |config|  
  config.after(:each) do
    if Rails.env.test? || Rails.env.cucumber?
      FileUtils.rm_rf(Dir["#{Rails.root}/spec/support/uploads"])
    end 
  end
end  

Cài đặt asset_host

Ta nên cài đặt asset_host cho carrierwave trong file # config/initializers/carrierwave.rb như sau:

CarrierWave.configure do |config|  
  config.asset_host = ActionController::Base.asset_host
end  

Đến đây, ta đã hoàn thành việc cấu hình sử dungh carrierwave trong testing. Nội dung cuối cùng của file config/initializers/carrierwave.rb sau khi chỉnh sửa là:

if Rails.env.test? || Rails.env.cucumber?

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

  # make sure uploader is auto-loaded
  FileUploader

  CarrierWave::Uploader::Base.descendants.each do |klass|
    next if klass.anonymous?
    klass.class_eval do
      def cache_dir
        "#{Rails.root}/spec/support/uploads/tmp"
      end

      def store_dir
        "#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
      end
    end
  end
end

CarrierWave.configure do |config|  
  config.asset_host = ActionController::Base.asset_host
end

Trên đây là cách sử dụng carrierwave trong factory girl và rspec. Cảm ơn các bạn đã dành thời gian theo dõi bài viết và hy vọng nó sẽ giúp ích trong công việc của mọi người.