如何在 Rails 中使用 ActiveStorage 正确进行模型测试?

47

我刚刚开始在Rails 5.1.4上使用ActiveStorage,对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)>'

有什么提示吗?


3
TIP: expect(valid_user.image.attached?).to be true 因为你不需要测试 ActiveStorage 的内部。 - Daniel
6
expect(valid_user.image).to be_attached 的意思与原来相同,只是更符合 RSpec 的惯用语法。 - chriswoodford
9个回答

34

我使用以下方法解决了问题

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

2
这会给你可能的假阳性,因为图像始终是ActiveStorage :: Attached :: One的实例,即使它为nil。在控制台中尝试以下内容:User.new.avatar。请注意,它是ActiveStorage :: Attached :: One的实例。 - stephenmurdoch
1
@stephenmurdoch:那么解决方案是什么? - Piezo Pea

20

问题已解决。在追踪错误到ActiveStorage :: Blob :: upload方法时,它说:将io上传到此blob的密钥上的服务。 我意识到我没有为测试环境设置active_storage.service。简单来说,只需添加:

config.active_storage.service = :test

要配置/config/environments/test.rb文件


2
尝试了另一个答案但没有成功 - 这个对我有用,谢谢 - Mark
有没有办法在一些模型使用Paperclip而另一些模型使用Activestorage? - t56k

13

这是我如何解决它的方法

# 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

我希望它能帮到你


这对我来说是更好的测试,你实际上检查了物品是否已连接。谢谢。 - NLxDoDge

5

在这些文章的帮助下,我更新了Rails 6.1中Active Storage的rspec测试。

# .\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

现在,您只能添加更多的测试。


4
我遇到了类似的错误(Module::DelegationError: to_model委托给attachment,但attachment为nil),但只有在多个测试使用相同工厂时才会出现。
看起来问题是上一个测试的拆卸清理正在删除文件,而在下一个工厂调用中附加文件之后。关键是将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

我刚刚升级到Rails 7.1,然后出现了这个问题。你的帖子救了我!!!谢谢你。 - undefined

3
在 config/environments/test.rb 中, config.active_storage.service = :test 在你的 spec 文件中, it {expect(valid_user.avatar).to be_attached} 其中,保留了 HTML 标签。

2
在我的情况下,我使用了Rails Shoulda宝石。
it { should have_one_attached(:image) }

1
为什么不呢:

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

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

1
你的回答可以通过添加更多关于代码的信息以及它如何帮助提问者来改进。 - Tyler2P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接