Joel Brewer
Joel Brewer

Reputation: 1652

Factory Girl - How to create a factory for a model that has associations?

I am attempting to create a factory for my user model, along with its associations. However, I cannot seem to get the syntax right in my Factory Girl code. I've read through the Factory Girl documentation but cannot seem to find any help with my specific use case. The errors I am currently receiving when I run my test suite are:

undefined method `subscription_args' for #<FactoryGirl::SyntaxRunner...

and

Trait not registered: valid_card_data

Here are my models and associations:

User.rb

has_one :subscription
has_one :plan, :through => :subscription
has_many :projects

Project.rb

belongs_to :user

Plan.rb

has_many :subscriptions

Subscription.rb

belongs_to :plan
belongs_to :user

and

And here is my Factory Girl code:

FactoryGirl.define do

  factory :user do
    first_name "Joel"
    last_name "Brewer"
    email { "#{first_name}.#{last_name}@example.com".downcase }
    password "foobar"
    password_confirmation "foobar"
    user_type "entrepreneur"

    subscription { build(:subscription, subscription_args) }

    after(:create) do |user|
      user.subscription.save!
    end
  end

  factory :subscription do
    user
    plan_id '4'

    ## I am trying to access a helper method from support/utilities ##
    ## This call to valid_card_data doesn't seem to be working...   ##

    stripe_card_token valid_card_data 
    email "[email protected]"
  end

  factory :project do
    title "Sample Project"
    user
  end
end

Upvotes: 1

Views: 668

Answers (2)

Kieran Andrews
Kieran Andrews

Reputation: 5885

There are several ways to do it. Here is one example:

after(:build) do |keyword, evaluator|
  keyword.text = FactoryGirl.build(:keyword_text, :value => evaluator.keyword_text)
end

You dont need subscription_args - these can be set when you call the factory.

Where are you defining your trait?

In my factories they look like this:

trait :with_category_associations do
  ..

For more complicated relationships you probably want to use:

  after(:create) do |keyword, evaluator|
    evaluator.categories.each do |category|
      FactoryGirl.create(:join_inventory_keyword, final: keyword, category: category)
    end
  end

Upvotes: 1

Kent Rancourt
Kent Rancourt

Reputation: 1595

Here's how I've done it in the past. Certainly not the only way:

(Note I am using cucumber.)

require 'factory_girl'

FactoryGirl.define do

  factory :user do |f|
    f.username 'superman'
  end

  factory :message do |f|
    f.association :user
    f.content 'Test message content'
  end

end

This establishes that the message factory should associate the message to a user. Which user? I establish that at the point of use:

steps.rb:

Given(/^there is a user$/) do
  @user = FactoryGirl.create(:user)
end

Given(/^the user has posted the message "(.*?)"$/) do |message_text|
  FactoryGirl.create(:message, :content => message_text, :user => @user)
end

When(/^I visit the page for the user$/) do
  visit user_path(@user)
end

Then(/^I should see "(.*?)"$/) do |text|
  page.should have_content(text)
end

My approach, specifying at the point of use makes sense for this use case. e.g. Given is a user (user must be established first) and that user has posted a message (now the relationship between the existing user and the message can be established)...

That may or may not work out well for you, but it's how I've done it. This may or may not have helped you, but here's hoping.

Upvotes: 2

Related Questions