投稿背景
架空モデルが関係する部分でテストを実装していたところ、架空モデルの定義でエラーが解決できずにいて手こずっていたが、なんとか解決できたので備忘録として投稿
実装する機能
・チャット機能
・通知機能←こいつが強敵でした
モデルのアソシエーション
touristとguide、2種類のユーザー間で通知機能を実装するためのアソシエーション。
chat_notice.rb
belongs_to :guide_visitor, class_name: 'Guide', foreign_key: 'visitor_id', optional: true
belongs_to :guide_visited, class_name: 'Guide', foreign_key: 'visited_id', optional: true
belongs_to :tourist_visitor, class_name: 'Tourist', foreign_key: 'visitor_id', optional: true
belongs_to :tourist_visited, class_name: 'Tourist', foreign_key: 'visited_id', optional: true
tourist.rb
has_many :tourist_active_notices, class_name: 'ChatNotice', foreign_key: 'visitor_id', dependent: :destroy
has_many :tourist_passive_notices, class_name: 'ChatNotice', foreign_key: 'visited_id', dependent: :destroy
guide.rb
has_many :guide_active_notices, class_name: 'ChatNotice', foreign_key: 'visitor_id', dependent: :destroy
has_many :guide_passive_notices, class_name: 'ChatNotice', foreign_key: 'visited_id', dependent: :destroy
factory
chat_notice.rb
FactoryBot.define do
factory :chat_notice do
association :chat
association :guide_visitor
association :guide_visited
association :tourist_visitor
association :tourist_visited
end
end
実行したテスト
chat_notice.rb
require 'rails_helper'
RSpec.describe ChatNotice, type: :model do
let(:chat) { create(:chat) }
let(:room) { create(:room) }
let(:guide_visitor) { create(:guide) }
let(:guide_visited) { create(:guide) }
let(:tourist_visitor) { create(:tourist) }
let(:tourist_visited) { create(:tourist) }
let!(:chat_notice) { build(:chat_notice, chat_id: chat.id, visited_id: tourist_visited.id, visitor_id: guide_visitor.id) }
describe 'ChatNotice保存テスト' do
context 'チャット通知が正しく保存される' do
it '全て入力されているので保存' do
expect(chat_notice).to be_valid
end
end
end
describe 'ChatNoticeアソシエーションのテスト' do
context 'Tourist_visitorモデルとの関係' do
it 'N:1となっている' do
expect(ChatNotice.reflect_on_association(:tourist_visitor).macro).to eq :belongs_to
end
end
context 'Tourist_visitedモデルとの関係' do
it 'N:1となっている' do
expect(ChatNotice.reflect_on_association(:tourist_visited).macro).to eq :belongs_to
end
end
context 'Guide_visitorモデルとの関係' do
it 'N:1となっている' do
expect(ChatNotice.reflect_on_association(:guide_visitor).macro).to eq :belongs_to
end
end
context 'Guide_visitedモデルとの関係' do
it 'N:1となっている' do
expect(ChatNotice.reflect_on_association(:guide_visited).macro).to eq :belongs_to
end
end
end
end
エラー内容
こんな感じでキーが見つからないよーっていうエラーが出る。
# KeyError:
# key not found: "guide_visitor"
guide_visitorはguideモデルを基に定義された架空のモデルだから、factoryにguideを定義していればテストは通ると思ってたけど、失敗。
原因
factory の定義の中で association を定義するということはその同名の factory が存在すること前提になってるみたいで、guide_visitorをfactoryに定義してなかったからエラーが発生してた。
解決方法 : factory:キーワード引数
factory の association 定義に対して、それが利用することになる association 先の factory の名前を別のものにしたい場合にはfactory:キーワード引数で指定してやる
FactoryBot.define do
factory :chat_notice do
association :chat
association :guide_visitor, factory: :guide #factory: :guideを追加
association :guide_visited, factory: :guide #factory: :guideを追加
association :tourist_visitor, factory: :tourist #factory: :touristを追加
association :tourist_visited, factory: :tourist #factory: :touristを追加
checked { Faker::Boolean.boolean }
end
end