Using factory girl to create HABTM association
Asked Answered
D

2

7

I've been trying now for hours to get factorygirl to create two factories - one for users, one for organizations.

But I don't seem to understand how I can reflect a 'has_and_belongs_to_many' relationship in factories, as soon as I try to create an organization and associate it with an admin user, I run into various error messages (depending on the approach I use). My model seems to be working fine, my seed file populates the dev DB and all the associations are created.

Right now my files look like this:

user factory

FactoryGirl.define do

  factory :user do
    email '[email protected]'
    password 'password'
    password_confirmation 'password'
    after(:create) {|user| user.add_role(:user)}

    factory :owner do
      after(:create) {|user| user.add_role(:owner)}
    end

    factory :admin do
      after(:create) {|user| user.add_role(:admin)}
    end

    factory :superadmin do
      after(:create) {|user| user.add_role(:superadmin)}
    end
  end
end

Organization factory

FactoryGirl.define do

  factory :organization do |f|
    f.name "example"
    f.website "www.aquarterit.com"
    f.association :users, :factory => :admin
  end
end

in my specs I test this:

describe Organization do


  it "has a valid factory" do
    FactoryGirl.create(:organization).should be_valid
  end

  it "is invalid without a name" do
    FactoryGirl.build(:organization, name: nil).should_not be_valid
  end

  it "is associated with at least one admin user" do
    FactoryGirl.create(:organization)
    it { should have_and_belong_to_many(:users)}
  end

end

all three tests are failing, here are the error message:

1) Organization has a valid factory
     Failure/Error: FactoryGirl.create(:organization).should be_valid
     NoMethodError:
       undefined method `each' for #<User:0x007fadbefda688>
     # ./spec/models/organization_spec.rb:7:in `block (2 levels) in <top (required)>'

  2) Organization is invalid without a name
     Failure/Error: FactoryGirl.build(:organization, name: nil).should_not be_valid
     NoMethodError:
       undefined method `each' for #<User:0x007fadc29406c0>
     # ./spec/models/organization_spec.rb:11:in `block (2 levels) in <top (required)>'

  3) Organization is associated with at least one admin user
     Failure/Error: organization = FactoryGirl.create(:organization)
     NoMethodError:
       undefined method `each' for #<User:0x007fadc2a3bf20>
     # ./spec/models/organization_spec.rb:15:in `block (2 levels) in <top (required)>'

Any help is as always very much appreciated!

Update

In theory the same thing that works for assigning roles to the user should work for assigning an admin to the organization. But if I change organizations.rb to

FactoryGirl.define do

  factory :organization do
    name "example"
    website "www.aquarterit.com"
    after(:create) {|organization| organization.add_user(:admin)}
  end
end

I get following error (I do have gem shoulda installed):

1) Organization is associated with at least one admin user
     Failure/Error: it { should have_and_belong_to_many(:users)}
     NoMethodError:
       undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1:0x007ff2395f9000>
     # ./spec/models/organization_spec.rb:16:in `block (2 levels) in <top (required)>'
Drida answered 8/2, 2014 at 20:12 Comment(2)
I think this post answers your question: #1484874Monarchism
Thanks - I found that one before, but as far as I understand it's not using the latest syntax; factorygirl now recommends using after(:create) to add an association, doesn't it? Updated the original question to include an amended organization factoryDrida
C
18

Looks like you are not assigning users correctly and not creating the :admin user properly. For this to work, you need to assign an array of users to organization.users. And, you need to populate that array with a User instance (this assumes you have a User factory named :admin).

factory :organization do
  name "example"
  website "www.aquarterit.com"
  after(:create) {|organization| organization.users = [create(:admin)]}
end
Crisp answered 8/2, 2014 at 21:21 Comment(0)
A
1

I do it this way, questions and tests have a HABTM relationship so:

FactoryGirl.define do
  factory :question do
   question  'Some stupid question'
   user nil
   factory :question_with_test do
     # factory_girl's dynamic attributes, ignore it and pass it to evaluator
     transient do
       test nil
     end

     after(:create) do |question, evaluator|
       create(:question_tests, question: question, test: evaluator.test)
     end
   end
  end
end

Now I can create a question with HABTM to the Test model:

let(:test)             { FactoryGirl.create :test, user: user }
let(:question_test_1)  { FactoryGirl.create :question_with_test, user: user, test: test }

The question_tests factory is very basic:

FactoryGirl.define do
  factory :question_tests, class: 'QuestionTest' do
    question
    test
  end
end
Agamogenesis answered 6/6, 2017 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.