モデルのバリデーションのテストをダラダラ書きたくない
RSpecでモデルのバリデーションのテストをダラダラ書かないで済むうまいやり方ってあるんでしょうか?
ダラダラ書かないで済む方法を模索してみました。
spec_helper.rbにこういうのを追加します。ここではバリデーションの種類はverify_hours(入力値が24(時間)以内かチェックする)だけあります。
種類を追加するときは、verify_hoursをまるっとコピーしたメソッドをつくって、valid_values、invalid_valuesの配列にそれぞれ有効値、無効値を書くだけです。
def verify_valid(klass, col_name, value) valid_check = Proc.new { attributes = @valid_attributes.merge(col_name.to_sym => value) obj = klass.new(attributes) obj.should be_valid obj.should have(0).errors_on(col_name.to_sym) obj.should have(0).errors obj.save.should be_true } end def verify_invalid(klass, col_name, value) invalid_check = Proc.new { attributes = @valid_attributes.merge("#{col_name}" => value) obj = klass.new(attributes) obj.should be_invalid obj.should have(1).errors_on(col_name.to_sym) obj.should have(1).errors obj.save.should be_false } end def verify_hours(klass, col_name) valid_values = [0, 3.5, 12, 24] invalid_values = [nil, "", "hankaku_moji", "全角文字", -1, -2.3, 25] valid_values.each{|value| it "#{col_name} 正常(#{value.inspect})", &verify_valid(klass, col_name, value) } invalid_values.each{|value| it "#{col_name} 異常(#{value.inspect})", &verify_invalid(klass, col_name, value)} end
モデルのSpecにはこんな感じで書きます。
before(:each) do @valid_attributes = { #各項目がvalidになる値を定義しておく } end describe "target_attributeは1日の時間であること" do verify_hours(TargetClass, :target_attribute) end
出力はこんな感じになります。
TargetClass target_attributeは1日の使用時間であること - target_attribute 正常(0) - target_attribute 正常(3.5) - target_attribute 正常(12) - target_attribute 正常(24) - target_attribute 異常(nil) - target_attribute 異常("") - target_attribute 異常("hankaku_moji") - target_attribute 異常("全角文字") - target_attribute 異常(-1) - target_attribute 異常(-2.3) - target_attribute 異常(25)
ちょっと調べてみたところ、accept_values_forというマッチャを追加するよいソリューションがありました。
accept_values_for
describe User do subject { User.new(@valid_attributes)} it { should accept_values_for(:email, "john@example.com", "lambda@gusiev.com") } it { should_not accept_values_for(:email, "invalid", nil, "a@b", "john@.com") } end
これはいいですね。