BPruvost
BPruvost

Reputation: 503

Best way to set up nested associations with RSpec / FactoryGirl

I am trying to build a test suite for a Rails app, and I'm not sure how to build objects with FactoryGirl (I can move to a different tool if necessary).

The tricky part is that there are have deep nested associations, lets take for example this part of the schema : https://i.sstatic.net/VbHbU.jpg

Let's say I want to do some tests on the "ItemTracker" model. Now there are 2 approaches :

So I guess my question is : what is the best way to set up some objects for testing in this kind of complex object structure ?

Thanks for the feedback ;)

Upvotes: 5

Views: 1811

Answers (2)

zetetic
zetetic

Reputation: 47578

I want the ProjectItem's project to be the same as the Token's project. Same thing with Company (on top). I wouldn't want to have 2 different projects or companies created

You can use a "named" factory by overriding initialize_with:

factory :project do
  # define common attributes for projects

  factory :project_1 do
    initialize_with do
      Project.find_or_create_by(name: "Project 1")
    end
  end
end

Now you can add through an association:

association :project, factory: project_1

and get the same project in associations on different models.

Note that this smells a lot like a fixture, so you might consider if fixtures would work better than factories for your situation.

I would just create the objects and associations I actually need. By playing with traits, I found a way to avoid creating the associations if I don't need them. The drawback is that this object will not be valid

I recommend creating a 'base' version of each factory that just allows you to set attributes, without associations, even if that is invalid. Then extend that to create valid (and potentially invalid) versions by adding associations. Jeiwan's answer is an good example.

Upvotes: 1

Jeiwan
Jeiwan

Reputation: 954

You need to build this whole relation tree anyway, but you can put this into a trait and, in specs, create item tracker with trait with_all_relations, for example. It would look like this:

FactoryGirl.define do
  factory :item_tracker do
    ...
    trait :with_all_relations do
      after(:build) do |item_tracker|
        company = create(:company)
        item = create(:item, company: company)
        user = create(:user, company: company)
        ...
        item_tracker.project_item = project_item
        item_tracker.connexion = connexion
      end
    end
  end
end

And if you specify no trait, you'll get item tracker without any related models.

Upvotes: 2

Related Questions