【问题标题】:Activestorage fixtures attachmentsActiveStorage 固定装置附件
【发布时间】:2018-10-31 09:22:27
【问题描述】:

在 Rails 测试中。我有一个只有活动存储的基本模型:

class User < ApplicationRecord
  has_one_attached :avatar
end

我正在尝试让它成为固定装置,但没有运气(我确实有一张图片):

# users.yml
one:
  avatar: <%= File.open Rails.root.join('test', 'files', 'image.png').to_s %>

如何通过fixture正确附加头像文件?

【问题讨论】:

    标签: ruby-on-rails rails-activestorage


    【解决方案1】:

    假设您对模型用户进行了测试,默认为UserTest#test_the_truth

    rails test test/models/user_test.rb

    我想你遇到了一个错误 Errno::ENOENT: No such file or directory @ rb_sysopen 在测试期间,由于您的路径错误, 一定要加'fixtures',应该是这样的:

    # users.yml
    one:
      name: 'Jim Kirk'
      avatar: <%= File.open Rails.root.join('test', 'fixtures', 'files', 'image.png').to_s %>
    

    但现在你应该有这个错误:ActiveRecord::Fixture::FixtureError: table "users" has no column named "avatar".

    没错,因为 ActiveStorage 使用两个表来工作:active_storage_attachmentsactive_storage_blobs


    因此,您需要从users.yml 中删除头像列并添加两个新文件:

    (免责声明另见下面的 cmets:“无需创建自己的模型,因此您可以使用原始的 ActiveStorage::Attachment 代替 ActiveStorage::Attachment 并将夹具放在 active_storage 文件夹下”,另请参阅 @987654321 @)

    # active_storage_attachments.yml
    one:
      name: 'avatar'
      record_type: 'User'
      record_id: 1
      blob_id: 1
    

    # active_storage_blobs.yml
    one:
      id: 1
      key: '12345678'
      filename: 'file.png'
      content_type: 'image/png'
      metadata: nil
      byte_size: 2000
      checksum: "123456789012345678901234"
    

    另外,在 App/models 中添加,即使 ActiveStorage 不需要工作

    # active_storage_attachment.rb
    class ActiveStorageAttachment < ApplicationRecord
    end
    

    # active_storage_blob.rb
    class ActiveStorageBlob < ApplicationRecord
    end
    

    那么UserTest#test_the_truth就成功了。

    但最好摆脱 active_storage_attachment.rbactive_storage_blob.rb 并按照另一种方式进行测试。

    为了测试附件是否工作,最好测试控制器,例如在test/controllers/users_controller_test.rb中添加此代码:

    require 'test_helper'
    
    class UserControllerTest < ActionController::TestCase
      def setup
        @controller = UsersController.new
      end
      test "create user with avatar" do
        user_name = 'fake_name'
        avatar_image = fixture_file_upload(Rails.root.join('test', 'fixtures', 'files', 'avatar.png'),'image/png')
        post :create, params: {user: {name: user_name, avatar: avatar_image}}
      end
    end
    

    检查文件夹tmp/storage,应该是空的。

    使用以下命令启动测试:rails test test/controllers/users_controller_test.rb

    应该成功了,那么如果你再次查看tmp/storage,应该会找到一些测试生成的文件夹和文件。

    在 cmets 之后编辑: 如果您需要在 User 模型上测试回调,那么这应该可以:

    # rails test test/models/user_test.rb
    
    require 'test_helper'
    
    class UserTest < ActiveSupport::TestCase
      test "should have avatar attached" do
        u = User.new
        u.name = 'Jim Kirk'
        file = Rails.root.join('test', 'fixtures', 'files', 'image.png')
        u.avatar.attach(io: File.open(file), filename: 'image.png') # attach the avatar, remove this if it is done with the callback
        assert u.valid?
        assert u.avatar.attached? # checks if the avatar is attached
      end
    end
    

    我不知道你的回调,但我希望这能给你一些提示。

    【讨论】:

    • 问题是我对模型进行了化身验证(这是必需的),并且我不想在每次测试之前附加它(这是我现在的解决方法)。这就是为什么我想把它们放在固定装置中。
    • User 模型不包含有关附件的任何信息,检索它的方法由has_one_attached 生成,与User 的关系在active_storage_attachment table 中。如果你想测试验证,你能提供那个代码吗?
    • 它本身没有经过验证,但如果不存在,它会附加一个头像after_validate on: :create,因此,当保存记录时,它总是有一个头像,这就是我在灯具中需要的。我不介意必须创建 active_storage_attachments 和 blobs yml 文件,只要我得到这种行为
    • @joseramonc,我编辑了在用户模型上添加测试,但我不知道这是否正是您需要的。
    • 不需要创建自己的模型,所以你可以用原来的ActiveStorage::Attachment代替ActiveStorageAttachment并将fixture放在active_storage文件夹下
    【解决方案2】:

    IS04 对我只想扩展的唯一答案发表了一条丢失的评论。

    一段时间以来,我一直在尝试执行此操作,并且遵循 iGian 的回答确实对我有用。然而,我的团队审查了我的 PR,并问我为什么要引入与 ActiveStorage 自己的模型命名如此接近的新模型(即ActiveStorage::AttachmentActiveStorage::Blob)。

    然后我突然想到,我需要做的就是将灯具从 active_storage_attachments.yml 移动到 active_storage/attachments.yml

    我必须通过额外研究弄清楚的另一部分是如何使用这些带有自动生成的 id 的固定装置。我使用ActiveRecord::FixtureSet.identify 这样做:

    attachment_identifier:
      name: "attachment_name"
      record_type: "MyRecordClass"
      record_id: <%= ActiveRecord::FixtureSet.identify(:my_record_identifier) %>
      blob_id: <%= ActiveRecord::FixtureSet.identify(:blob) %>
      created_at: <%= Time.zone.now %>
    

    【讨论】:

      【解决方案3】:

      这比任何人想象的要容易得多。我并不是要贬低任何人,因为我花了一些时间才根据这些答案弄清楚这一点。我将使用相同的数据模型来简化它。

      用户有一个附加的“头像”。假设你有这个用户装置:

      # users.yml
      fred:
        name: Fred
      

      这就是你需要做的所有事情:

      % mkdir test/fixtures/active_storage
      

      现在,您只需将“attachments.yml”和“blob.yml”放入该目录即可。 “附件”记录将引用 blob 以及用户(注意 name 条目是 has_one_attached 字段的名称):

      # active_storage/attachments.yml
      freds_picture:
        name: avatar
        record: fred (User)
        blob: freds_picture_blob
      

      # active_storage/blobs.yml
      freds_picture_blob:
        key: aabbWNGW1VrxZ8Eu4zmyw13A
        filename: fred.jpg
        content_type: image/jpeg
        metadata: '{"identified":true,"analyzed":true}'
        byte_size: 1000
        checksum: fdUivZUf74Y6pjAiemuvlg==
        service_name: local
      

      key 在代码中是这样生成的:

      ActiveStorage::Blob.generate_unique_secure_token
      

      您可以在控制台中运行它以获取上述夹具的密钥。

      现在,这将“起作用”以附上图片。如果您需要实际文件,请首先查看 config/storage.yml 以查看文件存储在哪个路径中。默认情况下,它是“tmp/storage”。上面的文件会存放在这里:

      tmp/storage/aa/bb/aabbWNGW1VrxZ8Eu4zmyw13A
      

      要计算校验和,请参见此处:

      How is the checksum calculated in the blobs table for rails ActiveStorage

      md5_checksum = Digest::MD5.file('tmp/storage/aa/bb/aabbWNGW1VrxZ8Eu4zmyw13A').base64digest
      

      可以在fixture中使用erb填写文件大小和校验和:

        byte_size: <%= File.size('tmp/storage/aa/bb/aabbWNGW1VrxZ8Eu4zmyw13A') %>
        checksum: <%= Digest::MD5.file('tmp/storage/aa/bb/aabbWNGW1VrxZ8Eu4zmyw13A').base64digest %>
      

      请注意,您必须先将文件复制到存储目录中。 测试环境的存储根目录默认为tmp/storage/,其余路径由key的前四个字符构成(即tmp/storage/aa/bb)。

      【讨论】:

      • 值得注意的是,service_name: %local%,其中 %local% 是来自config/storage.yml 的密钥,必须存在于blobs.yml 中才能使上述方法起作用。
      • 如上所述,只有在配置的保存路径下存在真实文件时,该解决方案才完全有效。鉴于tmp/ 路径被认为是短暂的(通常被 git 忽略),我找不到任何可靠的方法来确保此类 Active Storage 测试模拟正常工作。
      • @MichaelChaney 感谢您的回答!我已经部分基于它对 Rails 进行了 PR:github.com/rails/rails/pull/41543
      • 非常感谢@AlexGhiculescu!刚刚在您更新的指南中添加了答案
      • 在 Rails 6.0.4.1 中,我需要从 blobs.yml 中删除 service_name,因为它抛出了 table "active_storage_blobs" has no columns named "service_name" 错误。
      【解决方案4】:

      感谢@Alex Ghiculescu 打开 Rails 的 PR,让我知道“Active Storage 的测试套件是如何做到的”。 不幸的是,该代码似乎不在 6.1 分支中,但它们有一个 ActiveStorage::FixtureSet

      与此同时,您可以将其添加到您的 test_helper.rb 中(或者您想要组织代码:

      class ActiveStorage::Blob
        def self.fixture(filename:, **attributes)
          blob = new(
            filename: filename,
            key: generate_unique_secure_token
          )
          io = Rails.root.join("test/fixtures/files/#{filename}").open
          blob.unfurl(io)
          blob.assign_attributes(attributes)
          blob.upload_without_unfurling(io)
      
          blob.attributes.transform_values { |values| values.is_a?(Hash) ? values.to_json : values }.compact.to_json
        end
      end
      

      现在您可以将 freds-picture.jpg 添加到 test/fixtures/files 和您的夹具文件中,如下所示: test/fixtures/active_storage/attachments.yml

      freds_picture:
        name: picture
        record: fred (User)
        blob: freds_picture_blob
      

      test/fixtures/active_storage/blobs.yml

      freds_picture_blob: <%= ActiveStorage::Blob.fixture(
        filename: "freds-picture.jpg"
      ) %>
      

      希望这是有道理的,一旦 ActiveStorage::FixtureSet 在您的 Rails 版本中,您可以删除 self.fixture 方法并在您的夹具 yaml 文件中将 ActiveStorage::Blob.fixture 替换为 ActiveStorage::FixtureSet.blob

      对我来说绝对有用,加载一个在系统测试中呈现夹具的视图可以正确呈现图像。

      【讨论】:

        【解决方案5】:

        现在(rails 7),根据rails guides,似乎正确的方法是:

        # config/storage.yml
        
        test_fixtures:
          service: Disk
          root: <%= Rails.root.join("tmp/storage_fixtures") %>
        
        # active_storage/users.yml
        david:
          name: David
        
        # active_storage/attachments.yml
        david_avatar:
          name: avatar
          record: david (User)
          blob: david_avatar_blob
        
        # active_storage/blobs.yml
        
        david_avatar_blob: <%= ActiveStorage::FixtureSet.blob filename: "david.png", service_name: "test_fixtures" %>
        

        【讨论】:

        • 这是一个边缘指南,在发布时将适用于 rails 6.2。目前,在最新的 6.1.4.1 中这不起作用,因为 ActiveStorage::FixtureSet 不存在。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-06-17
        • 2021-05-02
        • 2019-05-13
        • 1970-01-01
        • 1970-01-01
        • 2020-03-01
        • 2019-08-04
        相关资源
        最近更新 更多