Search code examples
ruby-on-railsrails-activestorage

Activestorage fixtures attachments


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

class User < ApplicationRecord
  has_one_attached :avatar
end

I'm trying to make it's fixture, but having no luck with (I do have an image there):

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

How do I properly attach an avatar file through fixtures?


Solution

  • 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 (note that the name entry is the name of the has_one_attached field):

    # 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==
      service_name: local
    

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