Friday, March 9, 2012

Factory Girl Automatic Tests

Introduction
Early in a project I started to use factory girl without fully understanding it.  After many months of creating steps like "Given /^(\d+) blog exists$/" and "Given /^the following blogs exist:$/" I started to come up with generic functions that would build those steps.

Stupid me for not checking that factory girl already does something like that.  All you have to do is include factory girl's step_definitions.rb file:

Once you start using FactoryGirl correctly there are a world of new features that can make your steps both cleaner and more concise.  Here are some tips I have found via trial and error.

Tip 1: Do not reinvent the wheel
Factory girl will create steps for all factories that you have register.

Tip 2: Use association, Do not add more steps then are needed
Factory girl association are automatically created before the factory is created and they are automatically linked.  It only supports the belongs_to behavior, so keep that in mind.

Using the "Given the following XXX exist" step you can define attributes, on the the association, in the table.  If we leave the association out then a default is created.  If we define an attribute then it will be found or created using that attribute.

Lets say you have a product and it can belog to a category.  You do not need to create a category.

Tip 3: Attaching files via CarrierWave
Since cucumber is a text file it doesn't make much sense for you to define full files in steps.  It also doesn't really make sense to embed full file paths in the tests.  Instead, you can use a Transient attribute and some code so that in cucumber you define a file name and in the factory it converts to an actual file.

Tip 4: Fixing a circular dependency between two models
Lets say you have a Store model and User model.  And a User can both work at and own a Store.  If you put associations in both the User and the Store model then each will try to create the other, infinitely.  We can reuse the transient method as before to break the circle.

The trick is to avoid defining an association in both factories, but instead use a transient attribute in one factory to simulate the behavior of an association.  Also, since transient attributes are not likely to have the same level of sophistication as the associations you should use the association to define the more complex of the two models.

Tip 5: Fixing a circular dependency between the same model
Let say you have a Category model, and that model can belong to another Category (a tree) then you cannot use an association or you get the same infinite recursion issue as before.  Here we can use a transient attribute along with an after_create hook to simulate the behavior we want.

No comments:

Post a Comment