Write Faster Tests with a Factory Context
At OrgSync, we test our Ruby code with RSpec and factory_girl. A while back, I noticed our tests were slowing down for no apparent reason. It turns out our factories were creating a bunch of duplicate objects behind the scenes. For example, the event factory creates all these objects:
That’s 18 things: 1 event, 1 event category, 2 organizations, 2 umbrellas, 4 group types, and 8 schools. Of those, only 6 are needed. The other 12 are unnecessary duplicates.
It’s possible to avoid this by specifying the associations, which is what I did. I got fed up with manually doing that all the time and made a shared context. It ended up being much faster and a lot easier to use.
Here’s how, using Ruby 2.1.0p0, rspec 2.14.1, and factory_girl 4.3.0.
Let’s get started by writing some simple classes. We’re going to model a Reddit-style site with users, posts, and votes. Posts are submitted by users and users cast votes on posts.
Next we’re going to create factories for these classes. Just like the classes, they’re pretty simple.
Now we can use those factories in some tests. This particular test doesn’t do much, but it does show that two posts will be created.
Sometimes this is what you want, but usually it isn’t. To avoid creating extra objects, you need to specify all of the associations. That’s tedious and error-prone, especially as the number of objects increases.
So to keep from repeating yourself, put all the definitions in a shared context.
Then you can include the context and just start talking about the objects you want. You don’t have to build anything, and fewer objects will be created behind the scenes.
For example, this test creates half as many objects as the last one.
Let me repeat that: In this contrived example with three simple models, using the context created half as many objects. In a real test with real models, that would result in a significant speedup.
But what if we wanted to use
let! to eagerly load some objects? It looks like the factory context won’t let us do that. But it does — just talk about the objects that need to be loaded.
That’s not great, though. It’s not immediately obvious why those statements are there. We can do better by adding a helper method to the context.
Now you can use
preload when you want to eagerly load an object.
After switching to a factory context, writing tests got easier and running tests got faster. Plus we didn’t lose any expressiveness compared to the old way. What’s not to like?
[Originally posted to my blog.]
Taylor Fausak was born in California, but he got to Texas as soon as he could. He studied Computer Science at the University of Texas at Austin before entering the wild world of software development. After a brief stint at Cisco, he started his career at Famigo working on all aspects of web development. Then he swapped his Django experience for a chunky slice of Rails bacon and joined OrgSync in the fall of 2012. When he's not slinging code around, he likes riding bikes, playing Magic, and throwing frisbees.
comments powered by Disqus