Attaching ActiveStorage files in factorybot
Asked Answered
B

3

23

I'm looking for a way to create factories for models which have active storage attachments

I attempted the method in this post

with my factory

factory :activity_fit_file, class: 'Activity' do
    association :user, factory: :user
    activity_type {:cycling}
    original_activity_log_file { fixture_file_upload("#{Rails.root}/spec/files/example_fit_file.fit") }
end

but I got this error

 NoMethodError:
   undefined method `fixture_file_upload' for #<FactoryBot::SyntaxRunner:0x000000000208c5f8>

What is the correct way to attach files in ActiveStorage?

Brogan answered 13/3, 2019 at 12:7 Comment(0)
S
48

Try with Rack::Test::UploadedFile directly:

file { Rack::Test::UploadedFile.new('path', 'image/png') }
Staffman answered 13/3, 2019 at 12:21 Comment(7)
undefined method upload' for nil:NilClass # /home/user/.rvm/gems/ruby-2.5.1/gems/activestorage-5.2.0/app/models/active_storage/blob.rb:155:in upload'Brogan
Could be the file path, try with spec/files/example_fit_file.fitStaffman
Ok this was the solution but I forgot to set config.active_storage.service in my test envBrogan
This was working for me for years but after updating to Rails 6.1.3, it's getting an ActiveStorage::IntegrityError error. There isn't much information about that error either.Pianism
@Pianism I know it has been a while, but did you figure out what was causing the ActiveStorage::IntegrityError in this situation? I suddenly started seeing it on a project using Rails 5.2.0. The gems and configuration were not changed recently either.Eparchy
@Eparchy It actually wasn't the Rails upgrade but a Docker for Mac bug. I wrote about it here: github.com/rails/rails/issues/41991Pianism
@Pianism Thank you, I was using Docker as well, so it looks like the same bug is present on Docker for Linux too.Eparchy
L
37

This solution works with rails 6:

factory :post do
  # ...
  after(:build) do |post|
    post.image.attach(
      io: File.open(Rails.root.join('test', 'fixture_files', 'test.jpg')),
      filename: 'test.jpg',
      content_type: 'image/jpeg'
    )
  end
end
Lethalethal answered 23/7, 2021 at 10:21 Comment(2)
This should be the accepted solution for Rails 6+. Note that you can also use FactoryBot transient attributes to pass attachment-related information in with your model, e.g. after(:build) { |item, ev| item.image.attach(io: File.open(ev.image_path, 'rb'), filename: File.basename(ev.image_path), content_type: MiniMime.lookup_by_filename(ev.image_path)) if ev.image_path }Polyneuritis
I can confirm that this works. I switched to this after suddenly getting ActiveStorage::IntegrityError with the Rack::Test::UploadedFile approach after months without errors :shrugs:Eparchy
K
1

Change your code to the following it will work.

include ActionDispatch::TestProcess
factory :activity_fit_file, class: 'Activity' do
    association :user, factory: :user
    activity_type {:cycling}
    original_activity_log_file { fixture_file_upload("#{Rails.root}/spec/files/example_fit_file.fit") }
end
Karilynn answered 13/3, 2019 at 12:47 Comment(1)
It is not recommended to include ActionDispatch::TestProcess because it includes other methods such as #session which override existing methods on all objects. See: github.com/honeybadger-io/honeybadger-ruby/blob/… and this https://mcmap.net/q/584081/-include-actiondispatch-testprocess-prevents-guard-from-reloading-properlyStaffman

© 2022 - 2024 — McMap. All rights reserved.