Active Storage raises ActiveSupport::MessageVerifier::InvalidSignature

Rspec 遇上 Active Storage。

项目里文件上传部分使用了activestorage,写Rspec时,一直报错ActiveSupport::MessageVerifier::InvalidSignature,看文档查ActiveSupport::MessageVerifier::InvalidSignature, 懵逼,小坑,最后依靠强大的stack overflow解决了,记录下。

场景:

现有model attachment,含有字段file,title,另有一个model issue,issue与attachment是一对多的关系。

app/models/attachment.rb中,添加了:

class Attachment < ApplicationRecord
  belongs_to :issue, optional: true
  has_one_attached :file
end

app/models/issue.rb中,添加了:

class Issue < ApplicationRecord
  has_many :attachments, dependent: :destroy
  accepts_nested_attributes_for :attachments, allow_destroy: true
end

使用Rspec做测试时,用factorybot给Attachment建立测试数据,把测试使用的图片存放在spec/resources下,其中spec/factories/attachments.rb中的内容如下:

FactoryBot.define do
  factory :attachment do
    file { Rack::Test::UploadedFile.new(Rails.root.join("spec", "resources", "image.jpg") }
    title "this is a title"
  end
end

这里通过Rack ::Test::UploadedFile构建了file,这样在controller的spec中,传递给params的file就会被视为一个上传的文件。

现在需要测试issue中添加了attachment的场景。
spec/controllers/issues_controller_spec.rb中,有这样一段:

RSpec.describe IssuesController, type: :controller do
  let!(:issue) { create :issue }
  let(:issue_attributes) { {} }
  .....  
    describe "PUT update issue" do
      action { put :update, params: { id: issue, issue: issue_attributes } }

      context "with attachment" do
       let!(:attachment) { create :attachment, issue: issue }
       it expect(issue.attachments.count).to eq 1
      end
      ......
    end
end

此时运行,会报错,显示:

Failures:

  1) IssuesController PUT update issue with attachment should not be nil
     Failure/Error: @issue.update(issue_params)

     ActiveSupport::MessageVerifier::InvalidSignature:
       ActiveSupport::MessageVerifier::InvalidSignature

如果你在issues_controller.rb的issue_params中设置断点,会发现attachment虽然创建成功,但是attachment的file为空,也就是active storage并没有创建对应的blob, file并没有成功attached,可以通过attachment.file.attached?来判断file是否已经attached。

解决方法

最后找到的解决的方法其实很简单,来自stack overflow上How to properly do model testing with ActiveStorage in rails?, 只要修改一下attachment测试数据的生成方式即可,修改spec/factories/attachments.rb

FactoryBot.define do
  factory :attachment do
    title "this is a title"
    after(:build) do |attachment|
      attachment.file.attach(io: File.open(Rails.root.join("spec", "resources", "image.jpg")), filename: 'image.jpg', content_type: "image/jpg")
    end
  end
end

再次跑测试,会发现通过了。

参考

Active Storage raises ActiveSupport::MessageVerifier::InvalidSignature

How to properly do model testing with ActiveStorage in rails?

Active Storage attached one