Posted 11 months ago. Visible to the public.

Creating test data with factories [1d]

Goals

  • Learn to create test data effectively using factories.
  • Decouple tests by having each test start with an empty database and create only the records needed for the test.

Learn

Factories, not fixtures

By default Rails uses global fixtures Archive for its tests. This is a giant world of example data that grows with every test.

In our experience the use of fixtures can make a test suite hard to work with. In any non-trivial test suite there will be thousands of invisible dependencies between fixtures and test examples. E.g. to test a new edge case you make a small change to an existing fixture. Then a dozen tests break because they relied on the data you just changed.

Getting to know FactoryBot

Our favorite gem for test factories is FactoryBot Archive . We used other gems in the past, but they all work in the same way.

Watch Railscasts PRO #158 Factories not Fixtures (revised) Archive for an introduction to factories in general and FactoryBot in particular.

Note

This is an older video. FactoryGirl has since been renamed to FactoryBot.

Read the FactoryBot Getting Started guide Archive . Despite its name this is an in-depth guide and covers advanced use cases.

Factories should not be random

You may encounter libraries like Faker Archive that creates realistic names, addresses, etc. for your sample data. This looks awesome, but at the risk of adding random strings to your screen.

Read don't build randomness into your factories for more background.

Exercises

Setup FactoryBot

If you haven't done so already:

Tip

You will be calling your factories a lot. Therefore it is preferrable to just say create(:movie) instead of FactoryBot.create(:movie). You can configure this like so:

Copy
RSpec.configure do |config| config.include FactoryBot::Syntax::Methods end

Also see RSpec: Where to put custom matchers and other support code.

Use factories everywhere

  • Write a factory for each of your MovieDB models.
  • In your model specs and Cucumber features, only use factories to create your test data.
  • Leverage that factories will set defaults for attributes that you don't explicitly mention. You should remove any attribute values that are not relevant to a test. E.g. if a test needs a Movie but does not care about its #year attribute, the test should let the factory fill in a default year. However, if the test checks for one particular year, it should be explicitly set in the test (even though there is a factory default).

Advanced factory

Create a factory that works like this:

Copy
create(:movie, :biography, person: 'Al Gore')

This creates up to three records (class names may differ in your MovieDB implementation):

  • A Movie with with title "Al Gore: Biography"
  • An Actor with the name "Al Gore". If an actor with that name already exists, the record is re-used and no new Actor is created.
  • A Role that links the movie and actor above. The role's #character_name should also be "Al Gore", since he would be playing himself.

Tip

Don't actually give your Movie model a #person attribute.
Instead, use FactoryBot's transient attributes and before/after hooks to implement the factory above.

Your development team has a full backlog of feature requests, chores and refactoring coupled with deadlines? We are familiar with that. With our "DevOps as a Service" offering, we support developer teams with infrastructure and operations expertise.

Owner of this card:

Avatar
Henning Koch
Last edit:
2 months ago
by Fabian Schwarz
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Henning Koch to makandra Curriculum
This website uses short-lived cookies to improve usability.
Accept or learn more