【RSpec】モデル単体テストで、全カラムに対して.eachで同じ操作を実行したい
Railsで単体テスト書いてます。
例えばpresence: true
みたいな、たくさんのカラムに適用されているバリデーションって、一つ一つ書くのがめんどくさいですよね。
全カラムを配列で取得し、each文で回して、presence: true
バリデーションを一括でテストしてみました。
今回テストするモデル
item.rb
Column | Type | Options |
---|---|---|
user | references | null: false, foreign_key: true |
name | string | null: false |
information_text | text | null: false |
category_id | integer | null: false |
status_id | integer | null: false |
shipping_fee_status_id | integer | null: false |
prefecture_id | integer | null: false |
scheduled_delivery_id | integer | null: false |
sell_price | integer | null: false |
準備
FactoryBot
spec/factories/item.rb factory_bot_railsとfakerとrack-testを利用してテストデータを自動buildできるようにする。
rack-testで添付ファイルを使用するやり方はこちらの記事を参照
FactoryBot.define do factory :item do association :user name { Faker::Lorem.word } information_text { Faker::Lorem.sentence } category_id { Faker::Number.within(range: 1..10) } status_id { Faker::Number.within(range: 1..6) } shipping_fee_status_id { Faker::Number.within(range: 1..2) } prefecture_id { Faker::Number.within(range: 1..47) } scheduled_delivery_id { Faker::Number.within(range: 1..3) } sell_price { Faker::Number.within(range: 300..9999999) } image { Rack::Test::UploadedFile.new(File.join(Rails.root, 'public/images/test_image.jpg')) } end end
helper
次に、itemモデルのレコードとカラム名を文字列で渡すと、そのカラムをnilにしてエラーチェックしてくれるヘルパーを定義します。
spec/support/item_nil_support.rb
module ItemNilSupport def item_nil_validation(item, col, nihongo) item[col.to_sym] = nil item.valid? expect(@item.errors.full_messages).to include("#{nihongo}を入力してください") end end
ヘルパーメソッドの使い方についてはこちらの記事にまとめました。
カラムを配列で取得し、eachで回す
13行目:カラム名の配列はcolumn_namesメソッドで取得できます。今回チェックする必要の無いuser_idやcreated_dateなどは引き算で除いています。 eachのループの中でexample、すなわちit "..." do endを定義し、その中で先ほどのヘルパーを呼び出しています。
item_spec.rb
require 'rails_helper' RSpec.describe Item, type: :model do before do @item = FactoryBot.build(:item) end describe '商品新規登録' do #省略 context '新規登録がうまくいかないとき' do context 'nilのカラムが原因のとき' do #はじめにnilにしたいカラムの配列を作成する columns = Item.column_names - ["user_id", "id", "created_at", "updated_at"] columns.each do |col| nihongo = I18n.t "activerecord.attributes.item.#{col}" it "#{nihongo}が無ければ登録できない" do item_nil_validation(@item, col, nihongo) end end #省略 end
補足 (上の例のnihongo
について)
[11] pry(main)> I18n.t 'activerecord.attributes.user.nickname' => "ニックネーム"
今回のアプリケーションはデフォルトのlocaleがja
になっており、翻訳用のYAMLファイルは以下のようになっていました。
ja: activerecord: attributes: user: nickname: ニックネーム
この時、I18nクラスのt
メソッドを用いることで、指定の単語に対する翻訳を取得することができます。上の例では、ヘルパーメソッド内で、渡されたカラム名ごとにエラーメッセージを動的に出力させるのに使っています。
item_spec.rb
# colはループ変数で、単一のカラム名が入る nihongo = I18n.t "activerecord.attributes.item.#{col}"
spec/support/item_nil_support.rb
expect(@item.errors.full_messages).to include("#{nihongo}を入力してください")
[10/24追記] クラスがActiveRecordではなくAcriveModelを継承している場合
以下のように、モデルがActiveModelをincludeしている場合は、日本語訳の取得方法が異なります。
class Transaction include ActiveModel::Model attr_accessor :user, :item, :token, :postal_code, :prefecture_id, :city, :addresses, :building, :phone_number #省略 end
YAMLファイルと日本語訳の取得方法はそれぞれ以下のようになります。
ja: activerecord: #省略 activemodel: attributes: transaction: user: 購入者 item: 商品 #省略
[1] pry(main)> Transaction.human_attribute_name("user") => "購入者"