8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

「KeyError: Factory not registered:」に詰まった話

Posted at

 RSpecについて勉強している者です。

 現在制作中のオリジナルアプリにフォロー機能を実装しており、そのフォロー機能のモデルの単体テストを実装していました。その時に、「KeyError: Factory not registered:」というエラーが発生して苦戦したため、解決方法を自分用にメモを残しておこうと思います。
(といっても、振り返れば非常に単純なことでした😅)

 途中で見つけた解決策もご紹介していきます。参考になると幸いです。

エラーが発生した状況

エラーが発生した当時のコードを載せておきます。
(解決方法を手っ取り早く知りたい方は飛ばしてください)

app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :user
  belongs_to :follow, class_name: 'User'

  validates :user_id, presence: true
  validates :follow_id, presence: true
end
app/models/user.rb
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
spec/factories/relationships.rb
FactoryBot.define do
  factory :relationship do
    association :user
    association :follow
  end
end
spec/factories/user_spec.rb
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
spec/models/relationship_spec.rb
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に以下のような記述を追加するというものでした。

spec/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」という別名を命名したところ解決しました。

spec/factories/user_spec.rb
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'」のように、アソシエーションに別名を名付けている場合は、ファクトリの方にも別名をつけてあげる必要があったという話でした。

app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :follow, class_name: 'User'
end

 思えば、非常に単純な話でした😅
 
 もし、同じように「KeyError: Factory not registered:」に悩んでいる方がおられましたら、ご紹介した①や②の方法も試してみてください。実際、①②で解決した方もたくさんいるみたいです。

 もし間違っている点などありましたら、ご指摘いただけると幸いです。
 ここまで読んでいただき、ありがとうございました。

8
5
0

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
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?