概要
実際の業務でrspecを書いてるときに発生し、理解に少し時間がかかったので記事にしました。
テストは同じ結果をみているはずなのにと思っていましたが、ActiveRecordのオブジェクトの違いで落ちていました。
内容
実務とは異なりますが、下記のようなアソシエーションに新しくメソッドを定義してテストを書いていました。
class User < ApplicationRecord has_many :posts # 新しく定義してメソッド def published_posts posts.where(published: true) end end class Post < ApplicationRecord belongs_to :user end
テストは、以下のようにかきました。
RSpec.describe User, type: :model do describe '#published_posts' do let(:user) { User.create(name: 'John Doe') } let!(:post1) { Post.create(title: 'Post 1', user: user, published: true) } let!(:post2) { Post.create(title: 'Post 2', user: user, published: true) } let!(:unpublished_post) { Post.create(title: 'Unpublished Post', user: user, published: false) } it 'returns only published posts' do expect(user.published_posts).to eq(Post.where(user: user, published: true)) end end end
パッと見るとテストは通るように見えますが… 落ちます!
なぜか?
それは、期待値と結果のオブジェクト(クラス)が異なるからです。
# ActiveRecord::Associations::CollectionProxy user.published_posts # ActiveRecord::Relation Post.where(user: user, published: true)
ActiveRecord::Associations::CollectionProxy
これは、has_many
などのアソシエーションによって返されるオブジェクトです。なので、親に関連付けられた子のオブジェクトを返します。
user.posts user.posts.where(title: 'hogehoge')
ActiveRecord::Relation
これは、ActiveRecordでデータベースクエリを表すオブジェクトです。SQLを構築します。
Post.where(user: user, title: 'hogehoge')
上記のオブジェクトの違いが、テストを落とすのです。
回避策としては、下記が挙げられそうです。
- オブジェクトを揃える(今回は、
eq
の部分をActiveRecord::Associations::CollectionProxy
にする) to_a
で配列に揃える
私は、1つ目のほうが厳密にテストすることになるので良いのかなと思いました。
まとめ
最初は、なぜテストが落ちているのかわかりませんでしたが、失敗した箇所をよく確認することで見つけることができました。
Activerecordについて少し理解を深めることができたのでよかったです!
最後までお付き合いいただき、ありがとうございました!