Как правильно проводить тестирование модели с помощью ActiveStorage в рельсах?

Я только что перешел на использование ActiveStorage на рельсах 5.1.4, и я новичок в TDD и изо всех сил пытаюсь понять, как протестировать модель, которая 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

Но я получаю следующую ошибку:

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)>'

Любые подсказки?


person Donato Azevedo    schedule 30.11.2017    source источник
comment
СОВЕТ: expect(valid_user.image.attached?).to be true Потому что вам не нужно тестировать внутренности ActiveStorage.   -  person Daniel    schedule 01.12.2017
comment
expect(valid_user.image).to be_attached это то же самое, но немного более идиоматично RSpec   -  person chriswoodford    schedule 07.03.2018


Ответы (6)


Я решил использовать

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

После

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

  it { is_expected.to be_an_instance_of(ActiveStorage::Attached::One) }
end
person vgsantoniazzi    schedule 17.04.2018
comment
Это дает вам возможное ложное срабатывание, поскольку изображение всегда будет экземпляром ActiveStorage::Attached::One, даже если оно равно нулю. Попробуйте выполнить в консоли следующее: User.new.avatar. Обратите внимание, что это экземпляр ActiveStorage::Attached::One. - person stephenmurdoch; 08.10.2018

Проблема решена. После отслеживания ошибки в методе ActiveStorage::Blob::upload, где говорилось: Uploads the io to the service on the key for this blob. я понял, что не установил active_storage.service для тестовой среды. Проще говоря, просто добавьте:

config.active_storage.service = :test

В файл config/environments/test.rb

person Donato Azevedo    schedule 30.11.2017
comment
Пробовал другой ответ без радости - это сработало для меня, спасибо - person Mark; 19.09.2018
comment
Любой способ установить это, если некоторые модели имеют Paperclip и другие Activestorage? - person okay56k; 03.07.2019

Вот как я это решил

# 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

Я надеюсь, что это поможет вам

person axlfire1    schedule 05.05.2020
comment
Это был лучший тест для меня, вы действительно проверяете, прикреплен ли элемент или нет. Спасибо. - person NLxDoDge; 21.10.2020

В config/environments/test.rb

config.active_storage.service = :test

В вашей спецификации

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

person rexmadden    schedule 11.01.2019

Я сталкивался с похожей нулевой ошибкой (Module::DelegationError: to_model делегировано вложению, но вложение равно нулю), но это происходило только тогда, когда несколько тестов использовали одну и ту же фабрику.

Проблема, по-видимому, заключается в том, что при очистке от разборки предыдущего теста удалялся файл после того, как он был прикреплен при следующем вызове factory. Ключевым моментом было обновление адаптера очереди ActiveJob до встроенного, чтобы файл сразу удалялся.

Добавьте обе эти настройки в 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
person joe    schedule 10.06.2020

Почему нет:


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