Activestorage fixtures attachments

前端 未结 3 1958
忘掉有多难
忘掉有多难 2021-01-01 15:01

In rails tests. I have a basic model with only activestorage:

class User < ApplicationRecord
  has_one_attached :avatar
end

I\'m trying

相关标签:
3条回答
  • 2021-01-01 15:26

    There's a lost comment by IS04 on the only answer that I just want to expand on.

    I got stuck trying to do this for sometime and following iGian's answer did work for me. However my team reviewed my PR and asked why I was introducing new models so closely named to ActiveStorage's own models (i.e. ActiveStorage::Attachment and ActiveStorage::Blob).

    It then occurred to me that all I needed to do was move the fixture from active_storage_attachments.yml to active_storage/attachments.yml.

    The other part I had to figure out with extra research was how to use these fixtures with the automatically generated ids. Which I did so using ActiveRecord::FixtureSet.identify like this:

    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 %>
    
    0 讨论(0)
  • 2021-01-01 15:31

    Lets say you have a test for the model user, the default UserTest#test_the_truth

    rails test test/models/user_test.rb

    I suppose you are getting an error Errno::ENOENT: No such file or directory @ rb_sysopen during the test, because of an error in your path, you must add 'fixtures', it should be like:

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

    but now you should have this error: ActiveRecord::Fixture::FixtureError: table "users" has no column named "avatar".

    That's correct, because ActiveStorage uses two tables to work: active_storage_attachments and active_storage_blobs.


    So, you need to remove avatar column from users.yml and add two new files:

    (Disclaimer See also comments below: "no need create own models, so instead of ActiveStorageAttachment you could use original ActiveStorage::Attachment and place fixture under active_storage folder" and refer also to https://stackoverflow.com/a/55835955/5239030)

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

    and

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

    Also, in App/models, add, even if not required for the ActiveStorage to work.

    # active_storage_attachment.rb
    class ActiveStorageAttachment < ApplicationRecord
    end
    

    and

    # active_storage_blob.rb
    class ActiveStorageBlob < ApplicationRecord
    end
    

    Then the UserTest#test_the_truth succeed.

    But better get rid of active_storage_attachment.rb and active_storage_blob.rb and follow another way to test.

    For testing if the attachment is working, better test the controller, for example adding this code in 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
    

    Check the folder tmp/storage, should be empty.

    Launch the test with: rails test test/controllers/users_controller_test.rb

    It should succeed, then if you check again in tmp/storage, you should find some folders and files generated by the test.

    Edit after comments: If you need to test the callbacks on User model, then this should work:

    # 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
    

    I don't know your callback, but I hope this gives you some hint.

    0 讨论(0)
  • 2021-01-01 15:48

    This is far easier than anybody is making it out to be. I don't mean to demean anybody, as it took me some time to figure this out based on these answers. I'm going to use the same data model to make it easy.

    User has one attached "avatar". Let's say you have this users fixture:

    # users.yml
    fred:
      name: Fred
    

    Here's all you need to do:

    % mkdir test/fixtures/active_storage
    

    Now, you just put "attachments.yml" and "blobs.yml" in that directory. The "attachment" record will reference the blob as well as the user:

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

    and

    # 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==
    

    The key is generated like this in code:

    ActiveStorage::Blob.generate_unique_secure_token
    

    You can run that in the console to get a key for the above fixture.

    Now, that will "work" to have an attached picture. If you need the actual file to be there, first look in config/storage.yml to see what path the files are stored in. By default, it's "tmp/storage". The file above will be stored here:

    tmp/storage/aa/bb/aabbWNGW1VrxZ8Eu4zmyw13A
    

    To calculate the checksum, see here:

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

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

    It would be possible to fill in the file size and checksum using erb in the fixture:

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

    Note that you have to copy the file into the storage directory first. The storage root directory for the test environment is tmp/storage/ by default, with the remaining path constructed from the first four characters of the key (i.e. tmp/storage/aa/bb).

    0 讨论(0)
提交回复
热议问题