18

I'm using Factory Girl/Rspec2/Rails 3.

In factories.rb, I have:

Factory.define :user do |user|
  user.name       'Some guy'
  user.email      'some_guy@somewhere.org'
  user.password   'password'
end

Factory.define :org_admin, :parent => :user do |user|
  user.email 'org_admin@somehwere.org'
end

Factory.define :user_with_membership_request, :parent => :user do |user|
  user.email 'user_with_membership_request@somehwere.org'
end

Factory.define :organization do |org|
  org.name        'MEC'
  org.description 'Mountain Equipment Co-op'
end

Factory.define :membership do |membership|
  membership.user { Factory(:user) }
  membership.organization { Factory(:organization) }
end

Factory.define :admin_membership, :parent => :membership do |membership|
  membership.user { Factory(:org_admin) }
  membership.is_admin true
  membership.status 'active'
end

Factory.define :membership_request, :parent => :membership do |membership|
  membership.user { Factory(:user_with_membership_request) }
  membership.status 'requested'
end

and then in my rspec test I have:

  it 'should accept the membership request' do
    @org_admin = Factory(:org_admin)
    test_sign_in(@org_admin)
    @organization = Factory(:organization)
    @membership_request = Factory(:membership_request)

    put :update, :organization_id => @organization.id, :id => @membership_request.id, :acceptance => 'approve'
    ...
  end

When I run the test, I get:

 Failure/Error: @membership_request = Factory(:membership_request)
 Validation failed: Name has already been taken

I understand the reason for the failure is because FactoryGirl is creating another organization (with the same name).

But what I'd like to do is create several memberships all associated with the same organization. How do I do that?

Thanks.

Sean

Sean Lerner
  • 389
  • 5
  • 9

3 Answers3

20

You could check for an existing organization and use it, or create one if none exists:

Factory.define :membership do |membership|
  membership.user { Factory(:user) }
  membership.organization { Organization.first || Factory(:organization) }
end

FactoryGirl 4+ update:

Factory.define do
  factory :membership do
    user { create(:user) }
    organization { Organization.first || create(:organization) }
  end
end

Another approach is to use unique identifiers (e.g.: names) for each factory that you want to reuse, then use initialize_with to generate it:

factory :organization_1 do
  ignore { organization_name 'Sample Organization 1' }
  name { organization_name }
  initialize_with { Organization.find_or_create_by_name(organization_name) }
end

Now any reference to :organization_1 will always retrieve the same Organization. Obviously, you must use distinct names for this to work.

GuilPejon
  • 971
  • 13
  • 19
zetetic
  • 47,184
  • 10
  • 111
  • 119
  • Thanks Zeteic. I've used this. It works for now. (although when there's more than one Org, I guess I'll need to find another solution.. another problem for another day". – Sean Lerner Oct 27 '10 at 20:38
4

There are two things. 1. You might still want to create unique names for Factory(:organisation) you can achieve that using Factory.sequence which will generate it uniquely for you. 2. You can pass in a Factory(:membership_request, :organization => @organization) to use the existing object instead of creating a new one.

Kunday
  • 1,041
  • 6
  • 9
  • Thanks Kunday. I've also used this.. it seems like I have to define more than anything basic in rspec test rather than in factories.rb. I still need to understand factories and how to use them at a higher level. I can only mark one of you correct, even though I've used both of your suggestions. – Sean Lerner Oct 27 '10 at 20:40
  • Cool Sean. From what i noticed of using factory_girl for the past few months is more than 50% of tests depend on some explicit data set up from the rspec test. But given the amount of data setup that you dont do when your model becomes complex its still ok i guess. Nice to hear you got it worked out. – Kunday Oct 27 '10 at 21:15
2

With mongoid you can take combine the use of the #find_or_create_by method with Factory.attributes_for and do something like this

factory :membership do
    organization { Organization.find_or_create_by(Factory.attributes_for(:organization))}
end

I'm sure ActiveRecord has something similar.

bayfieldcoder
  • 158
  • 2
  • 8