0

I am using Rails 5.0.0.rc1, ruby 2.3.0p0, factory_girl (4.7.0), factory_girl_rails (4.7.0), faker (1.6.3).

At my console, I do & get the following:

[1] pry(main)> q1 = FactoryGirl.create(:question)
   (0.2ms)  BEGIN
  SQL (1.1ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["email", "vince@harris.com"], ["created_at", 2016-05-13 00:41:03 UTC], ["updated_at", 2016-05-13 00:41:03 UTC]]
   (2.0ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.4ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["email", "wilhelm.crona@luettgen.net"], ["created_at", 2016-05-13 00:41:04 UTC], ["updated_at", 2016-05-13 00:41:04 UTC]]
   (1.4ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.4ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["email", "marlen@boyer.name"], ["created_at", 2016-05-13 00:41:04 UTC], ["updated_at", 2016-05-13 00:41:04 UTC]]
   (0.5ms)  COMMIT
   (0.2ms)  BEGIN
  SQL (0.3ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["email", "brain@medhurst.io"], ["created_at", 2016-05-13 00:41:04 UTC], ["updated_at", 2016-05-13 00:41:04 UTC]]
   (0.3ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["email", "rico.rowe@hudsonankunding.info"], ["created_at", 2016-05-13 00:41:04 UTC], ["updated_at", 2016-05-13 00:41:04 UTC]]
   (0.3ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.8ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["email", "zakary@ritchie.io"], ["created_at", 2016-05-13 00:41:04 UTC], ["updated_at", 2016-05-13 00:41:04 UTC]]

Here is my Question factory:

# == Schema Information
#
# Table name: questions
#
#  id                 :integer          not null, primary key
#  title              :string
#  body               :text
#  user_id            :integer
#  accepted_answer_id :integer
#  created_at         :datetime         not null
#  updated_at         :datetime         not null
#

FactoryGirl.define do
  factory :question do
    user
    association :accepted_answer, factory: :answer
    title { Faker::Lorem.sentence(3, true, 4) }
    body { Faker::Lorem.paragraphs(2, true) }
  end
end

Here is my Question model:

# == Schema Information
#
# Table name: questions
#
#  id                 :integer          not null, primary key
#  title              :string
#  body               :text
#  user_id            :integer
#  accepted_answer_id :integer
#  created_at         :datetime         not null
#  updated_at         :datetime         not null
#

class Question < ApplicationRecord
  belongs_to :user
  belongs_to :accepted_answer, class_name: "Answer"
  has_many :answers
end

Here is my User factory:

# == Schema Information
#
# Table name: users
#
#  id         :integer          not null, primary key
#  email      :string
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

FactoryGirl.define do
  factory :user do
    email { Faker::Internet.email }
  end
end

Here is my User model:

# == Schema Information
#
# Table name: users
#
#  id         :integer          not null, primary key
#  email      :string
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

class User < ApplicationRecord
  has_many :questions
  has_many :answers
end

Edit 1

Here is my Answer factory:

# == Schema Information
#
# Table name: answers
#
#  id          :integer          not null, primary key
#  body        :text
#  user_id     :integer
#  question_id :integer
#  created_at  :datetime         not null
#  updated_at  :datetime         not null
#

FactoryGirl.define do
  factory :answer do
    question
    user
    body { Faker::Lorem.paragraphs(2, true) }
  end
end

Here is my Answer model:

# == Schema Information
#
# Table name: answers
#
#  id          :integer          not null, primary key
#  body        :text
#  user_id     :integer
#  question_id :integer
#  created_at  :datetime         not null
#  updated_at  :datetime         not null
#

class Answer < ApplicationRecord
  belongs_to :question
  belongs_to :user
end

What could be causing that endless User creation loop?

marcamillion
  • 32,933
  • 55
  • 189
  • 380

1 Answers1

3

You have a circular dependency between your Question and Answer factories.

Question builds an answer association, and the answer association builds a question association which, in turn, builds an Answer association - ad infinitum.

The reason why you're seeing an infinite trace of INSERTs on User is because that's the first thing and only action that your current implementation of the Question factory can perform. It never has an opportunity to do anything else because it's stuck in infinite regress.

Like Dave's link suggests, the simplest fix may be to simply use after :build or after :create to delay building the association until the parent object is created. You can also omit declaring the answer or question references in the corresponding factory definitions in lieu of explicitly declaring them when you create the factory itself:

let(:question) { FactoryGirl.create(:question) }
let(:answer) { FactoryGirl.create(:answer, question: question) }

Personally, I much prefer the second approach for it's clarity. It also affords greater control over your test suite at the small cost of some additional code.

In my experience, it's worthwhile to be explicit on how your test data is being defined in the initialization stages of your test suite. Relying on FactoryGirl callbacks, conventions, and syntactic sugar can lead to unexpected behavior and confusing inconsistencies which are difficult to trace.

Anthony E
  • 11,072
  • 2
  • 24
  • 44