If you’re using Cucumber and you’re not using Transformations you’re doing it wrong. I just started using these recently and ran into a problem with creating records using factory_girl factories that made use of sequences. While trying to create multiple Authlogic user records with a unique email and unique single_access_token using a Cucumber table, the functionality of Factory.next(:email) wasn’t working correctly, I would always get the same e-mail address. Turns out it was an easy fix, just had to use lazy attributes in my factory.
The Scenario, Step Definition and Factory
1 2 3 4 5 6 7 8 9 10 11 12 | Scenario: Presenter List
Given the following presenters:
| Name | Bio | Website |
| Clayton | Rails dev @integrum | http://claytonlz.com |
| Chris | Scrum @integrum | |
And I am on the homepage
When I follow "Presenters"
Then I should see "Clayton"
And I should see "Rails dev @integrum"
And I should see "http://claytonlz.com"
Then I should see "Chris"
And I should see "Scrum @integrum" |
My Step Definition
This uses the Transformation table below:
1 2 3 4 5 | Given /^the following presenters:$/ do |table| table.each do |attrs| Factory.create(:user, attrs) end end |
This transformation takes a table like the one in my scenario above, and assigns the values to a hash using the actual model attribute names (Name isn’t an attribute on a user but name is). The regular cucumber step definition “consumes” this hash for each entry in the table and passes it to a Factory for creation.
1 2 3 4 5 | Transform /^table:Name,Bio,Website$/ do |table| table.hashes.map do |hash| {:name => hash[:Name], :bio => hash[:Bio], :website => hash[:Website]} end end |
My Factory
This is a pretty basic factory for an authlogic user model, I’m using factory_girl sequences to give me a “unique” e-mail and single access token, which are required by Authlogic.
1 2 3 4 5 6 7 8 9 | Factory.define :user do |user| user.email Factory.next(:email) user.name "" user.bio "" user.website "" user.password "password" user.password_confirmation "password" user.single_access_token Factory.next(:single_access_token) end |
The problem
The above scenario will fail when it tries to create the user records via the factories. You’ll see a validation error about how the user model requires a unique e-mail and single access token. You’ll be wondering, “hey why are my sequences working?”. When you inspect the log you’ll see that they are in fact NOT working.
The Quick Answer
The easy answer to this is that you need to use lazy attributes in your factory for the sequences so that they are loaded each time instead of once.
1 2 3 4 5 6 7 8 9 | Factory.define :user do |user| user.email { Factory.next(:email) } user.name "" user.bio "" user.website "" user.password "password" user.password_confirmation "password" user.single_access_token { Factory.next(:single_access_token) } end |
Notice the curly braces around the sequences
The Longer Answer
The cucumber rdoc explains the Transform functionality, albeit somewhat hard to understand.
Registers a proc that will be called with a step definition argument if it matches the pattern passed as the first argument to Transform. Alternatively, if the pattern contains captures then they will be yielded as arguments to the provided proc. The return value of the proc is consequently yielded to the step definition.
I think the issue comes from something with the way these Procs are created, called and also their scope with regard to the step definition etc. I don’t think my ruby-fu is strong enough to give a good explanation but maybe I’m going in the right direction.