LoginSignup
14

More than 5 years have passed since last update.

RSpecでModelのテストをするときは、リレーションをchange from/toとしてテストしないように注意する

Last updated at Posted at 2014-03-21

問題: なぜかfailするテスト

  it 'adds comment' do
    post = Post.create(text: 'Hello!')
    comment = Comment.new(text: 'Hi!')
    expect{post.comments << comment}.to change{post.comments}.from([]).to([comment])
  end

このテストを実行するとfailします。

result should have initially been [], but was #<ActiveRecord::Associations::CollectionProxy [#<Comment id: 1, text: "Hi!", post_id: 1, created_at: "2014-03-21 01:51:44", updated_at: "2014-03-21 01:51:44">]>
./spec/models/comment_spec.rb:7:in `block (2 levels) in <top (required)>'

なぜでしょうか?

failする原因: post.commentsは実は配列ではない

post.commentsは配列ではなくリレーションなので、比較される寸前までデータベースに問い合わせない、というのが主要な原因です。

post.comments << commentの実行前にpost.commentsはいったんRSpec側に実際のfromの値として保存されますが、まだデータベースに問い合わせていません。

そして、post.comments << commentの実行後にpost.commentsの値をfromtoのそれぞれで評価します。

しかし、どちらもこのタイミングでデータベースに問い合わせるので、fromにもデータベースに保存されたcommentが返ってきてしまいます。

結果として、「fromの値は[]ではなかった」と判断されてfailするわけです。

解決策: 明示的に配列に変換する

change{}の中をリレーションではなく、確実に配列として返すようにします。具体的にはpost.comments.to_aです。
こうすれば、post.comments << commentの実行前にデータベースへの問い合わせが発生し、[]が実際のfromとして保存されます。

  it 'adds comment' do
    post = Post.create(text: 'Hello!')
    comment = Comment.new(text: 'Hi!')
    expect{post.comments << comment}.to change{post.comments.to_a}.from([]).to([comment])
  end

参考情報

この問題は前回のQiita記事のテストを書き直している最中に遭遇しました。(原因を特定するまでに結構時間がかかってしまった・・・)

しっかり読めていませんが、たぶん同じようなことを言っている記事がこれです。

リレーションに関しては以下の書籍の「056 リレーションを理解する」を読むとわかりやすいです。

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
14