Nested Factories — Why I chose to use them

Diego Tabares
2 min readAug 1, 2021

So far I have been used Rspec + FactoryBot for the last couple of years as my testing suite for Rails apps. For scenarios with related objects where one always requires another I ended up with complex object setup before the actual tests, which is a burden to maintain (and refactor if required).

As that bothered me, I started to revisit my factories implementations. I found that my test setup were complex and my factories were very simple, just some random generated data with Faker (I love this gem btw), some associations and a couple of traits.

What I wanted was “simple” … use the flexibility that traits provides for attributes but for “has_many” associations … that’s when after going through FactoryBot’s documentation I learned about nested factories.

Photo by Pavel Neznanov on Unsplash

Nested factories, as the docs explain, allow you to create multiple factories for the same class without having to repeat common attributes.

So, enough said, let’s deep dive into the example.

We have a simple User class, which has many email addresses (we want to support multiple emails for a single user). The EmailAddress class has some simple scopes to ease out the queries.

The code would look something like this:

So what’s the big deal? Each time we want to test something related to the user, we will have to create both a user and at least one email_address for that user. This is when the nested factories helped a lot.

The EmailAddress Factory is pretty standard:

But the User factory is the one that leverages the power of nested factories:

With this factory, I was able to create users and their email addresses with just one line, versus having to always create them both separately, as shown below:

I hope this article helps you to create more complete factories while reducing your test setup complexity.

Happy Testing!

--

--

Diego Tabares

Currently studying Computer Engineering @Untref. Linux and Mac user. @Independiente Fan. Proud IBMer. My opinions are my own.