Donato Azevedo
Donato Azevedo

Reputation: 1418

How to properly do model testing with ActiveStorage in rails?

I have just switched to using ActiveStorage on rails 5.1.4 and I am new to TDD and struggling to figure out how to test a model that has_one_attached :avatar

require 'rails_helper'

RSpec.describe User, :type => :model do

  let (:valid_user) { FactoryBot.build(:user) }
  describe "Upload avatar" do
    context "with a valid image" do      
      it "saves the image" do
        valid_user.save!        
        saved_file = valid_user.avatar.attach(io: File.open("/home/ubuntu/workspace/spec/fixtures/files/avatar.jpg"), filename: "face.jpg", content_type: "image/jpg")
        expect(saved_file).to be_an_instance_of(ActiveStorage::Attachment::One)
      end
    end
  end 

end

But I am getting the following error:

Failures:

  1) User Upload avatar with a valid image saves the image
     Failure/Error:
       saved_file = valid_user.avatar.attach(io: File.open("/home/ubuntu/workspace/spec/fixtures/files/avatar.jpg"), filename: "face.jpg", 
                                             content_type: "image/jpg")


 NoMethodError:
   undefined method `upload' for nil:NilClass
   Did you mean?  load
 # /usr/local/rvm/gems/ruby-2.3.4/gems/activestorage-0.1/lib/active_storage/blob.rb:48:in `upload'
 # /usr/local/rvm/gems/ruby-2.3.4/gems/activestorage-0.1/lib/active_storage/blob.rb:21:in `block in build_after_upload'
 # /usr/local/rvm/gems/ruby-2.3.4/gems/activestorage-0.1/lib/active_storage/blob.rb:16:in `tap'
 # /usr/local/rvm/gems/ruby-2.3.4/gems/activestorage-0.1/lib/active_storage/blob.rb:16:in `build_after_upload'
 # /usr/local/rvm/gems/ruby-2.3.4/gems/activestorage-0.1/lib/active_storage/blob.rb:26:in `create_after_upload!'
 # /usr/local/rvm/gems/ruby-2.3.4/gems/activestorage-0.1/lib/active_storage/attached.rb:25:in `create_blob_from'
 # /usr/local/rvm/gems/ruby-2.3.4/gems/activestorage-0.1/lib/active_storage/attached/one.rb:9:in `attach'
 # ./spec/models/user_spec.rb:47:in `block (4 levels) in <top (required)>'

Any hints?

Upvotes: 50

Views: 31997

Answers (9)

Ahmad Bhatti
Ahmad Bhatti

Reputation: 29

In my case, I have used rails shoulda gem

it { should have_one_attached(:image) }

Upvotes: 2

vicentesantos
vicentesantos

Reputation: 17

Rspec.describe User, type: :model do
  describe 'relations' do
    it { is_expected.to have_one_attached(:avatar_attachment) }
  end
end

Upvotes: -1

Mikołaj Wittbrodt
Mikołaj Wittbrodt

Reputation: 463

With some help of these post I did an update of rspec test for active storage at rails 6.1

# .\spec\models\activestorage_spec.rb
require 'rails_helper'

RSpec.describe User, :type => :model do
  before(:each) do
    @user = FactoryBot.create(:user)
        @user.save!
        saved_file = @user.pictures.attach(io: File.open("./storage/test.jpg"), filename: "test.jpg", content_type: "image/jpg")
  end

  describe "Upload picture" do
    context "with a valid picture" do    

      it "saves the picture" do        
        expect(@user.pictures).to be_attached
      end

    end
  end
end

Now you can only add some more tests

Upvotes: 5

joe
joe

Reputation: 332

I was running into a similar nil error (Module::DelegationError: to_model delegated to attachment, but attachment is nil), but it was only occurring when multiple tests were using the same factory.

The problem appears to be that the cleanup from the previous test's teardown was deleting the file after it was attached on the next factory call. The key was updating the ActiveJob queue adapter to inline so that the file would be deleted right away.

Add both of these settings to config/environments/test.rb:

# Use inline job processing to make things happen immediately
config.active_job.queue_adapter = :inline

# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test

Upvotes: 7

brcebn
brcebn

Reputation: 1732

Why not:


RSpec.describe User, type: :model do
  describe 'relations' do
    it { is_expected.to have_one(:avatar_attachment) }
  end
end

Upvotes: 0

axlfire1
axlfire1

Reputation: 301

Here is how I solved it

# model
class Report < ApplicationRecord
  has_one_attached :file
end
# factory
FactoryBot.define do
  factory :report, class: Report do 
    any_extra_field { 'its value' }
  end
end
# spec
require 'rails_helper'
RSpec.describe Report, type: :model do
  context "with a valid file" do
    before(:each) do
      @report = create(:report)
    end

    it "is attached" do
      @report.file.attach(
        io: File.open(Rails.root.join('spec', 'fixtures', 'file_name.xlsx')),
        filename: 'filename.xlsx',
        content_type: 'application/xlsx'
      )
      expect(@report.file).to be_attached
    end
  end
end

I hope it help you

Upvotes: 13

rexmadden
rexmadden

Reputation: 266

In config/environments/test.rb

config.active_storage.service = :test

In your spec

it {expect(valid_user.avatar).to be_attached}

Upvotes: 3

vgsantoniazzi
vgsantoniazzi

Reputation: 499

I solved using

FactoryBot.define do
  factory :culture do
    name 'Soy'
    after(:build) do |culture|
      culture.image.attach(io: File.open(Rails.root.join('spec', 'factories', 'images', 'soy.jpeg')), filename: 'soy.jpeg', content_type: 'image/jpeg')
    end
  end
end

After

describe '#image' do
  subject { create(:culture).image }

  it { is_expected.to be_an_instance_of(ActiveStorage::Attached::One) }
end

Upvotes: 35

Donato Azevedo
Donato Azevedo

Reputation: 1418

Problem solved. After tracing the error to the ActiveStorage::Blob::upload method, where it said: Uploads the io to the service on the key for this blob. I realized I had not set the active_storage.service for the Test environment. Simply put, just add:

config.active_storage.service = :test

To config/environments/test.rb file

Upvotes: 20

Related Questions