n.milsht n.milsht - 1 month ago 15
Ruby Question

Quadruple nested child elements in FactoryGirl

I'm trying to create dummy data with FactoryGirl.

User has many posts, post has many videos, video has many comments.
Comment belong to video and user.
Video belongs to post and user.
Post belongs to user.

I would like to create at least 20 users, each with at least 10 posts, each post with at least 1 video, each video with at least 1 comment.

Thus far I have the following factories however I can't seem to get the videos or comments to work.

spec/factories/comments.rb



FactoryGirl.define do
factory :comment do
sequence(:body) { |n| "#{n}body" }
video
user
end
end


spec/factories/posts.rb



FactoryGirl.define do
factory :post do
sequence(:title) { |n| "#{n}title" }
date Date.today.strftime('%m/%d/%Y')
body Faker::Lorem.paragraph(3)
tags Faker::Lorem.words(4)
user

trait :with_videos do
after(:build) do |post|
create(:video, post: post)
end
end
end
end


spec/factories/users.rb



FactoryGirl.define do
factory :user do
first_name Faker::Name.first_name
last_name Faker::Name.last_name
sequence(:username) { |n| "#{n}username" }
sequence(:email) { |n| "#{n}email@website.com" }
phone Faker::PhoneNumber.phone_number
password Faker::Internet.password(6, 20)
country Faker::Address.country
state Faker::Address.state_abbr
city Faker::Address.city
zip Faker::Address.zip
seeking_coach true
accept_email true
accept_phone true
end
end


spec/factories/videos.rb



FactoryGirl.define do
factory :video do
sequence(:title) { |n| "#{n}title" }
sequence(:url) { |n| "https://www.youtube.com/watch?v=tYm_#{n}2oCVdSM" }
embed_id { "#{url}.split('=').last" }
post
user

trait :with_comments do
after(:build) do |video|
create(:comment, video: video)
end
end
end
end

Answer

I think in factories hook should be after(:create) instead of after(:build), e.g.:

after(:create) do |video|
  create(:comment, video: video)
end

Here is all updated factories:

spec/factories/users.rb

FactoryGirl.define do
  factory :user do
    first_name  Faker::Name.first_name
    last_name  Faker::Name.last_name
    sequence(:username) { |n| "#{n}username" }
    sequence(:email) { |n| "#{n}email@website.com" }
    phone Faker::PhoneNumber.phone_number
    password Faker::Internet.password(6, 20)
    country Faker::Address.country
    state Faker::Address.state_abbr
    city Faker::Address.city
    zip Faker::Address.zip
    seeking_coach true
    accept_email true
    accept_phone true

    trait :with_10_posts do
      after(:create) do |user|
        create_list(:post, 10, :with_videos, user: user)
      end
    end
  end
end

spec/factories/posts.rb

FactoryGirl.define do
  factory :post do
    sequence(:title) { |n| "#{n}title" }
    date Date.today.strftime('%m/%d/%Y')
    body Faker::Lorem.paragraph(3)
    tags Faker::Lorem.words(4)
    user

    trait :with_videos do
      after(:create) do |post|
        create(:video, :with_comments, post: post)
      end
    end
  end
end

spec/factories/videos.rb

FactoryGirl.define do
  factory :video do
    sequence(:title) { |n| "#{n}title" }
    sequence(:url) { |n| "https://www.youtube.com/watch?v=tYm_#{n}2oCVdSM" }
    post

    trait :with_comments do
      after(:create) do |video|
        create(:comment, video: video)
      end
    end
  end
end

spec/factories/comments.rb

FactoryGirl.define do
  factory :comment do
    sequence(:body) { |n| "#{n}body" }
    video
  end
end

And RSpec test to ensure that factories setup works:

describe 'Factories' do
  it 'creates 20 users, each with at least 10 posts, each post with at least 1 video, each video with at least 1 comment' do
    FactoryGirl.create_list(:user, 20, :with_10_posts)
    expect(User.count).to eq(20)
    user = User.take
    expect(user.posts.count).to be >= 10
    post = user.posts.take
    expect(post.videos.count).to be >= 1
    video = post.videos.take
    expect(video.comments.count).to be >= 1
  end
end

Here is all the code - https://github.com/shhavel/stackoverflow_question_40136020

Comments