Ajaxで手数料と利益の計算をするの巻② モデル単体テスト

JavaScript 実装が完了し、最後の詰めでモデルのバリデーションをかけ直しました。

販売価格は、

  • ¥300~¥9,999,999の間のみ保存可能であること。
  • 価格は半角数値のみ保存可能であること。

こちらは numericality でいけそう。

さっそく実装します。

こんな感じ。

値として数値のみを許す

only_integer: true

¥300~¥9,999,999の間のみ許可

greater_than_or_equal_to: 300,less_than_or_equal_to: 9_999_999

これで販売価格のバリデーション設定は完了! ちなみに、数字の間にあるアンダーバーは表示上数字を見やすくするためのもので、 データを取得しても『1_000』であれば『1000』となります。 Web上でカンマ区切りで表示させることもできます。

そして、画像で示してある通り、わたしはmessageオプションを使いました。 それが沼の始まりでした。 というか、バリデーションの仕組みを理解していなかったのがいけなかったんです。

たとえば、 only_integer: true でバリデーションをかけると、 デフォルトのエラーメッセージは「must be an integer」なんです。

同様に、 greater_than の場合は「must be greater than %{count}」、 less_than の場合は「must be less than %{count}」なんです。

Active Record バリデーション - Railsガイド

そこをもうみんな一緒でいいよねー、ってことで「is invalid」 無効です、にしてたのにテストファイルとモデルファイルのエラーメッセージの統合性が取れていなかったものだから エラー、エラーのオンパレードで、エラーメッセージの始まりは大文字でなければいけない、とか 「can't be blank」なのに「is valid」になっているとかでひとつずつエラーを解消していったのですが、 最後に残ったのが販売価格のところ。

販売価格は値が空ではいけない、半角数字でなければいけない、指定の範囲でなければいけない、と複数のバリデーションがかかっており ChatGPTに聞いてみたり検索したりする中でいろいろな意見があるわけです。 それらすべてを試してみて、最終的にこちら。

require 'rails_helper'

RSpec.describe Item, type: :model do
  before do
    @item = FactoryBot.build(:item)
  end
  
  describe '商品情報登録' do
    context '商品情報が登録できるとき' do
      it '正常に登録できるとき' do
        expect(@item).to be_valid
      end
    end
  
    context '商品情報が登録できないとき' do
      it 'itemが空では登録できない' do
        @item.name = ''
        @item.valid?
        expect(@item.errors.full_messages).to include "Name can't be blank"
      end
      it '商品画像が空では登録できない' do
        @item.image = nil
        @item.valid?
        expect(@item.errors.full_messages).to include "Image can't be blank"
      end
      it 'カテゴリーが空では登録できない' do
        @item.category_id = ''
        @item.valid?
        expect(@item.errors.full_messages).to include "Category can't be blank"
      end
      it '商品の状態が空では登録できない' do
        @item.condition_id = ''
        @item.valid?
        expect(@item.errors.full_messages).to include "Condition can't be blank"
      end
      it '配送料の負担情報が空では登録できない' do
        @item.postage_type_id = ''
        @item.valid?
        expect(@item.errors.full_messages).to include "Postage type can't be blank"
      end      
      it '発送元の地域情報が空では登録できない' do
        @item.prefecture_id = ''
        @item.valid?
        expect(@item.errors.full_messages).to include "Prefecture can't be blank"
      end
      it '発送までの日数情報が空では登録できない' do
        @item.shipping_time_id = ''
        @item.valid?
        expect(@item.errors.full_messages).to include "Shipping time can't be blank"
      end
      it '価格情報が空では登録できない' do
        @item.price = nil
        @item.valid?
        expect(@item.errors.full_messages).to include "Price can't be blank"
      end
      it '価格は¥299以下の場合は登録できない' do
        @item.price = '299'
        @item.valid?
        expect(@item.errors.full_messages).to include "Price is invalid"
      end
      it '価格は¥1,000,000以上の場合は登録できない' do
        @item.price = '10000000'
        @item.valid?
        expect(@item.errors.full_messages).to include "Price is invalid"
      end
      it '価格は半角数値でなければ登録できない' do
        @item.price = 'ああああ'
        @item.valid?
        expect(@item.errors.full_messages).to include "Price is invalid"
      end
    end
  end
end

やっと通りましたー!

エラーメッセージも適当に入れたらだめなのね、とかデフォルト値があるのね、とか めっちゃ勉強になりました。 さらに続く。