LoginSignup
2
0

More than 3 years have passed since last update.

【Rails6】単体テスト時、ActiveRecordの暗黙的型変換でハマった話

Last updated at Posted at 2020-09-24

事象

以下の単体テストが失敗してしまう。

item_spec.rb
    it 'priceが半角数字でなければ保存できないこと' do
      @item.price = '1000'
      @item.valid?
      expect(@item.errors.full_messages).to include('Price Half-width characters.')
    end

結論

以下2点を実施することで解決
①price_before_type_castに対してバリデーションを設定
②price_before_type_castがpriceとして、エラーメッセージに表示されるように設定

price_before_type_castとは、暗黙的型変換が行われる前のpriceの値が格納された項目です。
ActiveRecordには「カラム名_before_type_cast」という項目が標準で用意されております。
つまり現在、priceには数値「0」、
price_before_type_castには文字列「1000」が格納されていることになります。

本題に戻ります
①price_before_type_castに対してバリデーションを設定

item.rb
class Item < ApplicationRecord
  # 価格は半角数字のみ
  validates :price_before_type_cast,
  format: { with: /\A[0-9]+\z/, message: 'Half-width characters.' }
end

しかし、このままではエラーメッセージが「Price before type cast Half-width characters.」と表示されてしまい、単体テストはまたエラーとなります。Webページにも同じエラーメッセージが表示されてしまうため、困ります。
そこで②を実施します。

②price_before_type_castがpriceとして、エラーメッセージに表示されるように設定

application.rb
module アプリ名
  class Application < Rails::Application
    # 以下2行を追記
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml').to_s]
    config.active_model.i18n_customize_full_message = true
  end
end

locales/en.yml
en:
  activerecord:
    attributes:
      item:
        price_before_type_cast: Price

これでprice_before_type_castのバリデーションエラーはpriceとして表示されるようになります。

単体テストも無事に通りました🎉

原因

integer型の項目「price」への文字列「1000」代入時、
暗黙的型変換が行われ、priceには数値「0」が代入されたため。

↓暗黙的型変換が行われる様子をコンソールで確認↓

irb(main):002:0> @item.price = '1000'
=> "1000"
irb(main):003:0> @item.price
=> 0

文字列「1000」を代入した後のpriceの値は数値「0」となります。

これはRails 5から追加された「ActiveRecord Attributes API」という機能によるものです。
暗黙的型変換は裏で以下のようなセッターが動いているイメージです。
to_iしているところがポイントです。

  # セッター
  def price=(price)
    @price = price.to_i
  end

【参考リンク】

「ActiveRecord Attributes API」の仕組みについては、以下の記事を参考にさせていただきました。
https://www.wantedly.com/companies/wantedly/post_articles/31132

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0