こんにちは、鈴木です。
テストコードでバリデーションの検証を行うときの話です。
以下のような User クラスがあるとします。
1 2 3 4 5 6 7 8 9 |
class User < ActiveRecord::Base attr_accessible :name, ... validates :name, :presence => true, :length => {:allow_blank => true, :maximum => 20} end |
この User クラスのバリデーションのテストを書く場合に、どのようなテストコードを書きますか?
こんな感じ、・・・だとテストとして少し緩いですね。
1 |
assert User.new(:name => 'たろう').valid? |
できれば valid? の戻り値だけではなく、エラーの種類(blank なのか too_long なのか、など)も検証できると嬉しいです。
ということで、以下のようなメソッドを書いてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# # バリデーションエラーを検証する. # # ==== 詳細 # 以下の値が一致することを assert_equal で検証する. # # * [I18n.translate("activerecord.errors.messages.#{error_type}", replacements)] # * object.errors[attribute] # # error_type が nil の場合は, object.errors[attribute] が空であることを検証する. # # ==== 引数 # object:: 検証対象の ActiveRecord オブジェクト. # attribute:: 検証対象の属性名. # error_type:: エラーの種類 (:blank, :too_long, ...). # replacements:: エラーメッセージを置換する値 (Ex. {:count => 10}). # def assert_error_message(object, attribute, error_type, replacements={}) if error_type replacements = replacements.reverse_merge(:attribute => object.class.human_attribute_name(attribute)) assert_equal [I18n.translate("activerecord.errors.messages.#{error_type}", replacements)], object.errors[attribute] else assert_blank object.errors[attribute] end end |
上記メソッドを使用すると、User クラスのテストコードは以下のようになります。
1 2 3 |
# 名前が未入力の場合は NG. user = User.new.tap(&:valid?) assert_error_message user, :name, :blank |
1 2 3 |
# 名前を入力すれば OK. user = User.new(:name => 'たろう').tap(&:valid?) assert_error_message user, :name, nil |
1 2 3 |
# 名前が 20 文字の場合は OK. user = User.new(:name => 'x' * 20).tap(&:valid?) assert_error_message user, :name, nil |
1 2 3 |
# 名前が 20 文字を越える場合は NG. user = User.new(:name => 'x' * 21).tap(&:valid?) assert_error_message user, :name, :too_long, :count => 20 |
「assert User.new(:name => 'たろう').valid?」よりはだいぶ良くなったかと思います。
もっと良い方法が無いか、今も考え中です。