- 更新日: 2014年3月4日
- RSpec
RSpec で例外テストの書き方、および ActiveRecord の例外をテストする際の注意
昨日のエントリー、Rails モデルの RSpec 単体テストで validation と DB に対するテストを区別した | EasyRamble の補足エントリーになります。
— 環境 —
rails-4.0.1
rspec-rails-2.14.0
capybara-2.2.0
RSpec で例外をテストするコード
unique キー制約の付いていてるDBカラムに対して、以下のように例外の発生を確認するテストを書いたのですが、この時はまった箇所がありました。
spec/models/user_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
describe "with DB" do describe "column having unique-key constraint" do describe "when email address is already taken" do before { @user.save! } it "should raise error ActiveRecord::RecordNotUnique" do expect do user_with_same_email = @user.dup user_with_same_email.email = @user.email user_with_same_email.save! validate: false end.to raise_error( ActiveRecord::RecordNotUnique ) end end end #... end |
User モデルについて、email カラムが DB で unique 制約となっています。なので、既存のユーザーと email が同一のユーザーを保存しようとしたら、例外(ActiveRecord::RecordNotUnique)が発生することを意図して書いたテストコードです。このテストコードは、正しく動作します。
save と save! は違う
【追記 2014/12/08】
Rails勉強中さんにコメントを頂きまして、このパラグラフの記述が間違っていることに気付きましたので訂正します。save メソッドでも ActiveRecord::RecordNotUnique は raise され、save メソッドでは ActiveRecord::RecordInvalid のエラーだけが rescue されて false を返しているとのことです。なので、この記事内の ActiveRecord::RecordNotUnique の例外捕捉のテストでは、save! ではなく save を使っても構いません。Rails のソースコードを確認したところ、確かにそうなっていました。
save (ActiveRecord::Persistence) – APIdock
以降このパラグラフの間違えていた文章は、打ち消し線で訂正しておきます。Rails勉強中さん、ご指摘ありがとうございました!
【追記ここまで】
1 |
user_with_same_email.save! validate: false |
の行についてなのですが、最初以下のように書いていました。
1 |
user_with_same_email.save |
save メソッドは、レコード保存に失敗しても false を返すだけです。なので、何の例外も発生させず、以下のようにテストを通らず。
1 2 3 |
expected ActiveRecord::RecordNotUnique but nothing was raised |
レコード保存の失敗時に例外を返すためには、感嘆符付きの save! メソッドを使う必要があります。
validation のスキップには validate: false
1 |
user_with_same_email.save! |
上記コードを書いてテストを行ってみましたところ、次のエラー。
1 2 3 |
expected ActiveRecord::RecordNotUnique, got #<ActiveRecord::RecordInvalid: バリデーションに失敗しました。 Eメールはすでに存在します。> with backtrace: |
User モデルに書いてある validation に引っかかってしまい、DB に保存するところまで行ってません。なので、validation を無効にする必要がある。
ということで、最終的に以下のように変更。
1 |
user_with_same_email.save! validate: false |
これで、意図した通りに、ActiveRecord::RecordNotUnique を発生させるテストを実行できました。
- RSpec の関連記事
- FactoryGirlをSpringと共に使う時の注意
- 複数モデルのpost :createをテストするRSpecコード(Controller Spec)
- RSpec3でTime.nowをスタブ化(stub)
- RSpecでJSONによるPOSTリクエストをテスト
- RSpec & Capybara の雑感
- RSpec+Capybaraで同名の複数要素の並び順をテストする
- RSpec3ではrails_helper.rbがrequireされる
- Capybara + Launchy で RSpec テストをブラウザで確認
- CapybaraのwithinをRSpecで使う
- Serverspec(RSpec)のテスト出力に色を付けて見やすくフォーマット
- 2件のコメント
saveでもActiveRecord::RecordNotUniqueはraiseされますよん。
saveメソッドがrescueでfalse返しているのは、ActiveRecord::RecordInvalidのエラーだけなので。
データベースエラーやBugなのでraiseされた場合は、rescueされまえん
Rails勉強中さん、ご指摘ありがとうございます。訂正いたしました。