Advertisement
  1. Code
  2. TDD
Code

Factory Girl 201

Final product image
What You'll Be Creating

My second article about this popular and useful Ruby gem deals with a couple more nuanced topics that beginners don’t necessarily need to concern themselves with right away when they get started. Again, I did my best to keep it newbie-accessible and explain every bit of lingo people new to Test-Driven Development (TDD) might stumble over.

Topics

  • Dependent attributes
  • Transient attributes
  • Lazy attributes
  • Modifying factories
  • Callbacks
  • Associations
  • Aliases
  • Traits

Dependent Attributes

If you need to use attribute values for composing other factory attributes on the fly, Factory Girl has you covered. You just need to wrap the attribute value in a block and interpolate the attributes you need. These blocks have access to an evaluator—which is yielded to them—and which in turn has access to other attributes, even transient ones.

Transient Attributes

I think it’s fair to call them fake attributes. These virtual attributes also allow you to pass additional options when you construct your factory instances—via a hash of course. The instance itself won’t be affected by them since these attributes won’t be set on your factory object. On the other hand, Factory Girl treats transient attributes just like real ones.

If you use attributes_for, they won’t show up though. Dependent attributes and callbacks are able to access these fake attributes inside your factory. Overall, they are another strategy to keep your factories DRY.

The example above turns out to be a bit more DRY since there was no need to create separate factories for supervillains that want to save the world or are friends with Blofeld respectively. Transient attributes give you the flexibility to make all kinds of adjustments and avoid creating a host of ever so similar factories.

Lazy Attributes

“Normal” attributes in Factory Girl are evaluated when the factory is defined. You usually provide static values as parameters to methods with the same name as your attributes. If you want to delay the evaluation until the last possible moment—when the instance gets instantiated—you will need to feed attributes their values via a code block. Associations and dynamically created values from objects like DateTime objects will be your most frequent customers for that lazy treatment.

Modifying Factories

This is probably not a use case you’ll run into every day, but sometimes you inherit factories from other developers and you want to change them—in case you are using a TDD’d gem for example. If you feel the need to tweak these legacy factories to better fit your specific test scenarios, you can modify them without creating new ones or using inheritance.

You do this via FactoryGirl.modify, and it needs to be outside that particular FactoryGirl.define block that you want to change. What you can’t do is modify sequence or trait—you can override attributes defined via trait though. Callbacks on the “original” factory also won’t be overridden. The callback in your Factory.modify block will just be run as next in line.

In the example above, we needed our spies to be a bit more “sophisticated” and use a better mechanism to handle deployment. I have seen examples where gem authors had to deal with time differently and where it was handy to modify factory objects by simply overriding stuff you need to tweak.

Callbacks

Callbacks allow you to inject some code at various moments in the lifecycle of an object—like save, after_save, before_validation and so on. Rails, for example, offers a whole bunch of them and makes it pretty easy for novices to abuse this power.

Keep in mind that callbacks that are not related to the persistence of objects are a known anti-pattern, and it’s good advice to not cross that line. For example, it may seem convenient to use a callback after instantiating something like user to send emails or process some order, but these kinds of things invite bugs and create ties that are unnecessarily hard to refactor. Maybe that was one of the reasons why Factory Girl “only” offers you five callback options to play with:

  • before(:create) executes a code block before your factory instance is saved. Activated when you use create(:some_object).
  • after(:create) executes a code block after your factory instance is saved. Activated when you use create(:some_object).
  • after(:build) executes a code block after your factory object has been built in memory. Activated when you use both build(:some_object) and create(:some_object).
  • after(:stub) executes a code block after your factory has created a stubbed object. Activated when you use build_stubbed(:some_object).
  • custom(:your_custom_callback) executes a custom callback without the need to prepend before or after.

Attention!

Note that for all callback options, inside the callback blocks, you’ll have access to an instance of the factory via a block parameter. This will come in handy every once in a while, especially with associations.

Below, a Ninja has a bunch of nasty throwing stars (shuriken) at his disposal. Since you have a ninja object in the callback, you can easily assign the throwing star to belong to the Ninja. Take a peek at the section about associations if that example leaves you with a couple of question marks.

Also, through the evaluator object you have access to transient attributes too. That gives you the option to pass in additional information when you “create” factory objects and need to tweak them on the fly. That gives you all the flexibility needed to play with associations and write expressive test data.

If you find the need to have multiple callbacks on your factory, Factory Girl is not standing in your way—even multiple type ones. Naturally, order of execution is top to bottom.

The devil is in the details, of course. If you use create(:some_object), both after(:build) and after(:create) callbacks will be executed.

Multiple build strategies can be bundled to execute the same callback as well.

Last but not least, you can even set up something like “global” callbacks that override callbacks for all factories—at least in that particular file if you have separated them into multiple factory files.

factories/gun.rb

Attention!

If you use inheritance to compose child factories, the callbacks on the parent will be inherited as well.

The Last Mile

Let’s bring it all together in the following sections about associations and traits—yes, I also sneaked in alias because it was the best place without jumping all over the place. If you have paid attention and remember stuff from the first article, everything should fall quite neatly into place now.

Associations

Associations are essential to every self-respecting web app that has a little bit of complexity. A post that belongs to a user, a listing that has many ratings and so forth are the bread and butter developers have for breakfast any day of the week. Seen from that perspective, it becomes obvious that for more complex scenarios factories need to be bullet-proof and easy to handle—at least in order to not mess with your TDD mojo.

Emulating model associations via Factory Girl is relatively straightforward, I’d say. That in itself is quite amazing in my mind. Achieving a high level of ease and convenience for building complex data sets makes the practice of TDD a no-brainer and so much more effective.

The new Q has hacker skills and needs to own a decent computer, right? In this case you have a Computer class and its instances belong to instances of the Quartermaster class. Easy, right?

What about something a bit more involved? Let’s say our spies use a gun that has_many cartridges (bullets).

Callbacks come in pretty handy with associations, huh? Now you can build a gun with or without ammunition. Via the hash gun: gun you provided the cartridge factory with the necessary information to create the association via the foreign_key.

If you need another magazine size you can pass it in via your transient attribute.

So what about the different build strategies? Wasn’t there something fishy? Well, here’s what you need to remember: If you use create for associated objects, both of them will be saved. So create(:quartermaster) will build and save both Q and his ThinkPad.

I’d better use build, then, if I want to avoid hitting the database, right? Good idea, but build would only apply to quartermaster in our example—the associated computer would still get saved. A bit tricky, I know. Here’s what you can do if you need to avoid saving the associated object—you specify the build strategy you need for your association.

You name the associated factory object and pass in a hash with your build strategy. You need to use the explicit association call for this to work. The example below won’t work.

Now both objects use build and nothing gets saved to the database. We can check that assumption by using new_record?, which returns true if the instance hasn’t been persisted.

While we’re at it, via the explicit association call you can also refer to different factory names and change attributes on the fly.

Let’s close this chapter with an example that is polymorphic.

Don’t feel bad if this one needs a bit more time to sink in. I recommend catching up on Polymorphic Associations if you’re unsure what’s going on here.

Aliases

Aliases for your factories allow you to be more expressive about the context that you are using your factory objects in. You only need to provide a hash of alternative names that better describe the relationship between associated objects.

Let’s say you have an :agent factory and a :law_enforcement_vehicle factory. Wouldn’t it be nice to refer to the agent as :owner in the context of these cars? In the example below, I contrasted it with an example without an alias.

Attention!

Don’t forget to add a colon in front of the aliased factory (:owner) when you use them for associations in your factories. The documentation and many blog posts use them without colons in these cases. All you get is probably a NoMethodError because you are missing a setter method for that alias now. (I’d better open a pull request.) The first time I ran into this, it baffled me and took me a bit to get past it. Remember to sometimes selectively distrust documentation and blog posts. Yours truly as well, of course.

I think you’ll agree that using aliases it not only reads better but also gives you or the person who comes after you a bit more context about the objects in question. Yeah, you need to use plural :aliases also if you have only a single alias.

You could write this a bit differently as well—a lot more verbosely.

Well, not that neat, is it?

Of course you can use these aliases also to “build” factory objects right away.

In the context of comments, a :user could be referred to as :commenter, in the case of a :crime a :user could be aliased as a :suspect, and so on. It’s not rocket science really—more like convenient syntactic sugar that decreases your temptation for duplication.

Traits

This is one of my favorite things about Factory Girl. In a nutshell, traits are lego-like blocks to build your factories and mix in behaviour. They are comma-separated lists of symbol traits/attributes that you want to add on to a particular factory, and they are also defined in your factories’ file(s).

In my mind, trait is the most powerful and convenient function to keep your factory data DRY while being expressive at the same time. It allows you to bundle groups of attributes together, give them separate names, and reuse them wherever you please. Remember when I urged you to define bare-bones factory objects? Traits will help you achieve exactly that without sacrificing any convenience.

As you can see, if you want to change some attributes that are spread over multiple objects, you can do that now in one central place. No shotgun surgery necessary. Managing state via traits couldn’t be more convenient.

With that setup you can build pretty elaborate spy cars by mixing the various attribute bundles however you like—without duplicating anything via creating all sorts of new factories that account for all the various options you need.

You can use traits with create, build, build_stubbed and attributes_for. If Q gets clever, you can also override individual attributes simultaneously by passing in a hash.

For trait combinations that occur very frequently on a particular factory, you can also create child factories with names that best represent the various data set combos. That way you bundle their traits only once as opposed to all the time when you create test data.

This lets you create these objects more much concisely, and it’s more readable too.

Instead of:

Reads much better, no? Especially when no variable names are involved.

You can even reuse traits as attributes on other traits and factories. If you define the same attributes for multiple traits, the last one defined gets precedence, of course.

Pay attention to the mobile_surveillance trait, which reuses the cloaked and night_vision traits—basically as an attribute. Also the ultimate_spy_car factory, which I separated out of the spy_car factory definition for fun this time, reuses all traits plus an additional attribute that makes it fly too. Pure movie magic—or maybe I should say Factory Girl magic.

create_list and build_list can also make use of traits. The second parameter needs to be the number of factory instances you want.

Wouldn’t it be cool to use associations with traits? Of course you can pack callbacks and associations neatly into traits. Duh!

How to use them should be boring by now.

Final Thoughts

Last words of wisdom: Change is your constant companion—needing to change attributes or data types happens all the time. Design decisions like these evolve. Traits will ease the pain with that and help you manage your data sets.

Imagine if you’d used an options hash for instantiation and that requirement totally changed. How many potential places in your tests might break and will now need attention? Straight up, trait is a very effective tool for eliminating duplication in your test suite. But with all that convenience, don’t be lazy and forget your unit tests on the columns that are represented by your traits! That way you give them the same amount of care as the bare-bones attributes needed for valid objects.

There is a bit more to discover in Factory Girl, and I’m confident you are now more than well equipped to put the pieces together when you need them. Have fun playing with this gem. I hope your TDD habits will benefit from it.

Advertisement
Advertisement
Looking for something to help kick start your next project?

Envato Market has a range of items for sale to help get you started.