Active Storageの簡単なバリデーションの実装とテスト
今日はActive Storageにバリデーションを設定したので、 その内容をまとめておきます。
背景
Active Storageにはデフォルトのバリデーションというのがないようです。 (そのせいで、みんなCarrierWaveで実装しているのかな?)
Rails5.2から入る新機能ActiveStorageを使うべきか?
うーん、なんか移行した方が良さそうなのかな。。。
とはいえ、せっかくS3、clodfrontの設定までしたばかりなので、 ひとまずは簡単なバリデーションを実装しておこうと思います。
実装
今回はこちらの記事を参考に(ほぼまるパクリですみません)で実装しました。
validate :validate_avatar def validate_avatar return unless avatar.attached? # ファイルがアタッチされていない場合は何もしない if avatar.blob.byte_size > 10.megabytes avatar.purge # アタッチされたファイルの削除 errors.add(:avatar, 'ファイルのサイズが大きすぎます') elsif !image? avatar.purge # アタッチされたファイルの削除 errors.add(:avatar, 'ファイルが対応している画像データではありません') end end private def image? %w[image/jpg image/jpeg image/gif image/png].include?(avatar.blob.content_type) end
purgeというのがActiveStorageのAPIでアタッチされたファイルの削除を行います。
これとは別にdetachという実体ファイルは残したまま、関連を削除するといったものもあるようです。 (attachmentで関連を、blobで実体を管理しているというようなイメージかな?)
これで無事実装できました。 ただこれだけだとただの丸パクリなので、テストも実装しておきます。
テスト
単体テストを実装していきます。
始める前にファクトリで添付ファイルを扱えるように設定します。
# spec/rails_helper.rb FactoryBot::SyntaxRunner.class_eval do include ActionDispatch::TestProcess end # spec/factories/users.rb FactoryBot.define do ... trait :with_avatar do avatar { fixture_file_upload(Rails.root.join('spec', 'support', 'assets', 'test.png')) } end ... end
これでファクトリでActiveStorageの画像をテストすることができるようになりました。
この他にも
を用意しました。
あとはテストを書いていきます。
# spec/models/user_spec.rb require 'rails_helper' RSpec.describe User, type: :model do let(:user) { FactoryBot.create(:user) } let(:user_attached_image) { FactoryBot.create(:user, :with_avatar) } let(:user_too_large_avatar) { FactoryBot.build(:user, :with_too_large_avatar) } let(:user_attached_not_image) { FactoryBot.build(:user, :with_not_image) } describe "avatarのバリデーション", :focus do context "ファイルサイズが10MBを越える時" do it "ファイルサイズが大きすぎますというエラーメッセージが返る" do user_too_large_avatar.valid? expect(user_too_large_avatar.errors[:avatar]).to include 'ファイルのサイズが大きすぎます' end end context "ファイルの種類がjpeg, jpg, png, gif以外の時" do it "ファイルが対応している画像データではありませんというエラーメッセージが返る" do user_attached_not_image.valid? expect(user_attached_not_image.errors[:avatar]).to include 'ファイルが対応している画像データではありません' end end end end
これで(否定にする検証も込みで)テストがグリーンになったので、実装完了とします。