🔗

ActiveStorageの画像URLをテストする方法

2025/01/08に公開

2025年もついに明けてしまいましたね...

なにこれ

Ruby on RailsにはActiveStorageというとても便利なライブラリがあります。
https://railsguides.jp/active_storage_overview.html

ActiveStorageで保存した画像のURLをテストする際のTipsを記事にしたものです。

問題

ActiveStorageでは public: true をしない限り、ファイルを表示したりDLする際のURLを一時的なURLにします。

> hoge.image.url
=> "http://example.com/rails/active_storage/disk/eyJfcmFpbHMiOnsiZGF0YSI6eyJrZXkiOiJwdGw3a2FjcmN4MnU0NHJ4b2lnaGw3YTlla3E0IiwiZGlzcG9zaXRpb24iOiJpbmxpbmU7IGZpbGVuYW1lPVwic2NyZWVuc2hvdC5wbmdcIjsgZmlsZW5hbWUqPVVURi04JydzY3JlZW5zaG90LnBuZyIsImNvbnRlbnRfdHlwZSI6ImltYWdlL3BuZyIsInNlcnZpY2VfbmFtZSI6InByaXZhdGUifSwiZXhwIjoiMjAyNS0wMS0wN1QxMDo0MDo0NS4wMzZaIiwicHVyIjoiYmxvYl9rZXkifX0=--bc22b1b05e742b0209cfc68032987d586cd5059d/screenshot.png"

(テスト環境なのでdiskにファイルがあります)

このURLは一時的なURLであるため再度URLを吐き出すとURLが変わります。

> hoge.image.url
=> "http://example.com/rails/active_storage/disk/eyJfcmFpbHMiOnsiZGF0YSI6eyJrZXkiOiJwdGw3a2FjcmN4MnU0NHJ4b2lnaGw3YTlla3E0IiwiZGlzcG9zaXRpb24iOiJpbmxpbmU7IGZpbGVuYW1lPVwic2NyZWVuc2hvdC5wbmdcIjsgZmlsZW5hbWUqPVVURi04JydzY3JlZW5zaG90LnBuZyIsImNvbnRlbnRfdHlwZSI6ImltYWdlL3BuZyIsInNlcnZpY2VfbmFtZSI6InByaXZhdGUifSwiZXhwIjoiMjAyNS0wMS0wN1QxMDo0MTozMC4xNDFaIiwicHVyIjoiYmxvYl9rZXkifX0=--3c8b5e764d1c91b1107d440001ccdded04cdfaa2/screenshot.png"

↑の仕様だと、APIモードなどでRailsを動かしていてレスポンスが正しいかをテストする際に期待値と異なるためテストできねぇとなったのが今回の問題です。

解決方法

解決方法はいとも簡単で、一時的なURLは現在の時刻をベースにURLを生成しているため

before { freeze_time }

をテストの最初に入れれば解決します!!

ActiveStorageのコード

テスト時はAWS等の外部サービスではなくDiskサービスをしている事が多いと思います。

https://railsguides.jp/active_storage_overview.html#diskサービス

DiskサービスのURLを生成している箇所は以下です。

https://github.com/rails/rails/blob/14c115b120ed089331ff3dc13f36bd9129ced33d/activestorage/lib/active_storage/service/disk_service.rb#L116-L142

private_urlpublic: true にしていない場合は呼ばれ、 generate_url で実際にURLを生成しています。
rails_disk_service_url は以下で宣言されています

https://github.com/rails/rails/blob/14c115b120ed089331ff3dc13f36bd9129ced33d/activestorage/config/routes.rb#L13

↑はURLヘルパーのコードだったので generate_url に戻ります。
ActiveStorage.verifier.generate は以下です。

https://github.com/rails/rails/blob/cf6ff17e9a3c6c1139040b519a341f55f0be16cf/activesupport/lib/active_support/message_verifier.rb#L304-L310

serialize_with_metadata の返り値をencodeしているっぽいですね...
serialize_with_metadata は以下です。

https://github.com/rails/rails/blob/ea5db080a390eb094a75e8c1b0aaff31bf48e20e/activesupport/lib/active_support/messages/metadata.rb#L30-L41

generate_url ではhashのあとに expires_inblob_key を渡しているので wrap_in_metadata_legacy_envelope が呼ばれそうです。
その後に serialize_to_json を呼んでいますね。

https://github.com/rails/rails/blob/ea5db080a390eb094a75e8c1b0aaff31bf48e20e/activesupport/lib/active_support/messages/metadata.rb#L71-L76

https://github.com/rails/rails/blob/ea5db080a390eb094a75e8c1b0aaff31bf48e20e/activesupport/lib/active_support/messages/metadata.rb#L124-L126

という感じで expires_in を使ってencodeしたURLを生成しているっぽいですね... (分かった顔
public: true の場合は expires_in がnilなので生成したタイミングでURLの変更はなさそうです。

よいテストライフを!!

SMARTCAMP Engineer Blog

Discussion