RSpecについて勉強している者です。
現在制作中のオリジナルアプリにフォロー機能を実装しており、そのフォロー機能のモデルの単体テストを実装していました。その時に、「KeyError: Factory not registered:」というエラーが発生して苦戦したため、解決方法を自分用にメモを残しておこうと思います。
(といっても、振り返れば非常に単純なことでした😅)
途中で見つけた解決策もご紹介していきます。参考になると幸いです。
エラーが発生した状況
エラーが発生した当時のコードを載せておきます。
(解決方法を手っ取り早く知りたい方は飛ばしてください)
class Relationship < ApplicationRecord
belongs_to :user
belongs_to :follow, class_name: 'User'
validates :user_id, presence: true
validates :follow_id, presence: true
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
# あるユーザーがフォローしている他ユーザーとのアソシエーション(フォロー)
has_many :relationships, foreign_key: 'user_id', dependent: :destroy
has_many :followings, through: :relationships, source: :follow
# あるユーザーをフォローしている他ユーザーとのアソシエーション(フォロワー)
has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: 'follow_id', dependent: :destroy
has_many :followers, through: :reverse_of_relationships, source: :user
end
FactoryBot.define do
factory :relationship do
association :user
association :follow
end
end
FactoryBot.define do
factory :user do
nickname { Faker::Lorem.characters(number: 10) }
email { Faker::Internet.free_email }
password = "a12345"
password { password }
password_confirmation { password }
end
end
require 'rails_helper'
RSpec.describe Relationship, type: :model do
let!(:relationship) { FactoryBot.create(:relationship) }
it "関係性が有効であること" do
expect(relationship).to be_valid
end
it "user_idがnilの場合、関係性が無効であること" do
relationship.user_id = nil
expect(relationship).to_not be_valid
end
it "follow_idがnilの場合、関係性が無効であること" do
relationship.follow_id = nil
expect(relationship).to_not be_valid
end
end
この状態で「bundle exec rspec」を実行すると……
Failures:
1) Relationship 関係性が有効であること
Failure/Error: let!(:relationship) { FactoryBot.create(:relationship) }
KeyError:
Factory not registered: "follow"
# ./spec/models/relationship_spec.rb:4:in `block (2 levels) in <main>'
# ------------------
# --- Caused by: ---
# KeyError:
# key not found: "follow"
# ./spec/models/relationship_spec.rb:4:in `block (2 levels) in <main>'
このように
「KeyError: Factory not registered: "follow"」
というエラーが発生してしまいました。
噛み砕いて説明すると、
「spec/factories/relationships.rbに記述していた、"association :follow"の"follow"って何?」
と怒られているわけですね。
解決策
3つほど解決方法があったので紹介していきます(自分の場合、以下の①と②は効果がありませんでした)。
① spec_helper.rbに記述を追加する
参考にさせていただいた記事:https://qiita.com/tmyn470/items/2bd1616cdb21f16916fb
「KeyError: Factory not registered:」でGoogle検索をかけると結構記事が出てきました。
自分がまずはじめに知った解決策は、spec_helper.rbに以下のような記述を追加するというものでした。
RSpec.configure do |config|
config.before(:all) do
FactoryBot.reload
end
end
自分はこちらの記述を追加しても解決しませんでしたが、ほとんどの記事でFactoryBot関係が原因だと結論づけられていました。
それから、
「どうやらFactoryBotに原因がありそうだな」
と予想して調べていきました。
②ターミナルで「spring stop」を実行する
次に知った解決策は、「ターミナルで『spring stop』を実行する」というものです。
これはプログラミングスクールのカリキュラムで紹介されていた方法です。
この「spring stop」を実行する理由ですが、
「Railsに標準で導入されている『Spring』というGemがバックグラウンドで作動していて、たまにロードエラーを起こしてしまう。その時は、『spring stop』を実行し、Springを一時停止する必要がある」
とのことです。
早速実行してみました。しかし……
アプリケーションディレクトリ名 % spring stop
Spring is not running
「Spring is not running」と出てしまい、「KeyError: Factory not registered:」のエラーは相変わらずです。
③ aliasで別名を命名する(今回の解決方法)
こちらが、今回の解決策になります。
aliasを用いて、「spec/factories/user_spec.rb」内のuserに「follow」という別名を命名したところ解決しました。
FactoryBot.define do
# userに「follow」という別名を命名
factory :user, aliases: [:follow] do
nickname { Faker::Lorem.characters(number: 10) }
email { Faker::Internet.free_email }
password = "a12345"
password { password }
password_confirmation { password }
end
end
下記の「belongs_to :follow, class_name: 'User'」のように、アソシエーションに別名を名付けている場合は、ファクトリの方にも別名をつけてあげる必要があったという話でした。
class Relationship < ApplicationRecord
belongs_to :follow, class_name: 'User'
end
思えば、非常に単純な話でした😅
もし、同じように「KeyError: Factory not registered:」に悩んでいる方がおられましたら、ご紹介した①や②の方法も試してみてください。実際、①②で解決した方もたくさんいるみたいです。
もし間違っている点などありましたら、ご指摘いただけると幸いです。
ここまで読んでいただき、ありがとうございました。