Using Cucumber’s Table Transformations, you can easily build complex objects in a way that’s easy to read and understand for clients and developers alike.

My latest favorite feature of Cucumber are Table Transformations. I frequently use tables to build up complex objects and I’ve found that the regular old tables can be a little ugly, especially when your attribute names don’t make much sense on their own. I’ve also noticed that building up associations can be a little wonky, usually requiring more steps than seem necessary.

Conventional Table Usage

Let’s look at an example of how we could use a table, without transformations, to build up some objects for our scenario.

1
2
3
4
5
Scenario: Editing a Spirit
  Given I have a spirit with the following attributes:
    | spirit_type    | country_of_origin | age | brand        | lgcy_prod_sku | name       |
    | Scotch Whisky  | Scotland          | 12  | The Balvenie | SC38181       | DoubleWood |
    | Scotch Whiskey | Scotland          | 12  | The Macallan | SC38245       |            |
1
2
3
4
5
Given /^I have a spirit with the following attributes:$/ do |table|
  table.hashes.each do |attributes|
    Spirit.create!(attributes)
  end
end

Be sure to use create! in your tests to prevent false positives (Thanks Aslak!)

Now this is all fairly simple, and it looks pretty easy to implement, but I see some problems. First, what if you were actually friends with your DBA (bear with me) and you knew better than to have an attribute in your model like country_of_origin or spirit_type. Chances are those are going to be used by many other records and should be pulled out and made into their own Models, Country and SpiritType respectively.1

So what does our scenario look like with those two new models?

1
2
3
4
5
6
7
8
9
10
11
Scenario: Editing a Spirit
  Given I have a country with the following attributes:
    | name     | continent |
    | Scotland | Europe    |
  And I have a spirit type with the following attributes:
    | name           |
    | Scotch Whiskey |
  And I have a spirit with the following attributes:
    | age | brand        | lgcy_prod_sku | name       |
    | 12  | The Balvenie | SC38181       | DoubleWood |
    | 12  | The Macallan | SC38245       |            |

It’s a little more complex, for sure, but it’s not totally unmanageable. However, the key part that’s missing is how to link the two spirits with their spirit types and countries of origin.

You could add some more steps, but then you’ve got a conjunction step which is inflexible and brittle.

1
  And the spirit named "The Balvenie" is from "Scotland" and is a "Scotch Whiskey"

You could go back to your original step, and try to do some behind-the-scenes stuff to map country_of_origin to the correct country_id, but that gets messy too.

Transform Your Tables

The first step to making good use of table transformations is to make your tables more readable. Start by change the header row of your table to use meaningful representations of the real attribute names.

1
2
3
4
Given I have a spirit with the following attributes:
  | Spirit Type    | Country  | Age | Brand        | Legacy Product Code | Name       |
  | Scotch Whisky  | Scotland | 12  | The Balvenie | SC38181             | DoubleWood |
  | Scotch Whiskey | Scotland | 12  | The Macallan | SC38245             |            |

We’ve turned that weird lgcy_prod_sku attribute into something that your Product Owner can make sense of and we’ve be able to add Spirit Type and Country of Origin back to the table. Now let’s look at the transformation that makes this all work.

1
2
3
4
5
6
7
8
9
10
11
12
Transform /^table:Spirit Type,Country,Age,Brand,Legacy Product Code,Name$/ do |table|
  table.hashes.map do |hash|
    spirit_type = SpiritType.create!({:name => hash["Spirit Type"]})
    country = Country.create!({:name => hash["Country"]})
    spirit = Spirit.create!({:age => hash["Age"],
                            :brand => hash["Brand"],
                            :lgcy_prod_sku => hash["Legacy Product Code"],
                            :name => hash["Name"]})
 
    {:spirit_type => spirit_type, :country => country, :spirit => spirit}
  end
end

The transformation step definition looks a lot like a regular table step definition. There is a regular expression, like anything else in Cucumber, that has the same values as the header row in the table from our scenario. Just like the table step definition we have a table object which is just an array of hashes. We can go through each hash, do the actual transformation, and then return something to our table step definition. We are using map (same as collect) to return an array of hashes, which is just what the table step definition is expecting.

You will also see that we’re creating three different records, which we are returning in the hash we create at the end. Let’s go through those step-by-step:

  1. Create a spirit type object from the hash["Spirit Type"] value
  2. Create a country object from the hash["Country"] value
  3. Create a spirit object from the several related hash values
  4. Put all of our created objects into a hash

While we’ve created the objects, we still need to create the associations. I like to leave this for the table step definition rather than the transformation since I think it’s more obvious what’s going on with the values when you’re viewing the table step.

1
2
3
4
5
6
7
Given /^I have a spirit with the following attributes:$/ do |table|
  table.each do |group|
    spirit = group[:sprit]
    associations = {:country => group[:country], :spirit_type => group[:spirit_type]}
    spirit.update_attributes(associations)
  end
end

There are at least a dozen ways to get the spirit associated with the country and spirit type, so don’t feel like you have to follow this pattern every time. Since we’ve sent our table an array of hashes we can iterate over each hash, group, and work with the individual rows. Here’s how:

  1. Extract the spirit object from the hash
  2. Create another hash with the country and spirit type that Rails can make sense of
  3. Use update_attributes to update the spirit object with the new associations

Transformation Tradeoffs

We’ve been able to take our original multi-step scenario and simplify it to a single step. We are using the proper place, the step definitions, to do the associations and we have made our scenario much easier to read for non-developers working on the project. But what did we give up?

The biggest issue I’ve found with using table transformations is that they can be inflexible when you need to add more attributes to your dynamically created object. If you are writing features, using your table to setup objects and then realize that you need to add another attribute, you’re going to have to edit your table transformation step and how you create objects from the hash. When you take this a step further and try to have two different table definitions, you’ll be looking at having two nearly identical table transformations.2

If you’re not already using regular old Cucumber tables to create objects, use this guide to get started. If you are using Cucumber tables to create objects, try to re-factor one of your scenarios and use the table transformation strategy. Once you start using Cucumber tables and table transformations you’ll instantly improve the readability, portability and efficiency of your steps.

1 Ignore for now the issues with spirit_type and Rails Single Table Inheritance

2 I’m guessing that there is some way you can get around this with regular expressions and to have more flexible transformation table steps, but I haven’t tried it yet.

{ 3 comments }

Do You Make This Common Mistake When Estimating?

by Clayton on January 5, 2010

There’s a common mistake that many software developers make when estimating projects. Here’s how you can avoid falling into this trap.

When estimating a project using the Planning Poker method, many developers like to use a baseline estimate for a given task. For example, many developers use CRUD, the creating, displaying, editing and deleting of a Model as their baseline estimate. Once they’ve got their baseline in mind, it makes it easier to estimate other stories that are more domain specific, or so it seems.

Baseline Estimates are Broken

When you’re using a task like CRUD as a baseline for your estimations, you can easily skew the estimations of the other stories in the project. Let’s say we’re using a 3 point baseline for CRUD stories.

  1. As a user I should be able to upload a profile photo – 2
  2. As a user I should be able to CRUD movies I’ve seen – 3
  3. As a user I should be able to send a private message to another user – 5
  4. As a user I should be able to create a trivia quiz for a movie that I’ve seen – 8

In this first example, the stories are probably estimated fairly well and compared to each other, the complexity is quite relative. What happens if we add a few more easy stories or a few more difficult stories?

  1. As a user I should be able to see a contact e-mail on the home page – 1
  2. As a user I should be able download the menu as a PDF - 1
  3. As a user I should be able to read a privacy policy – 2
  4. As a user I should be able to CRUD restaurant reviews – 3

With easier stories added, the CRUD story is definitely the most complex, but compared to the others it is significantly more complex than seeing a link on a page or viewing some text.

  1. As a user I should be able to CRUD portfolio photos – 3
  2. As a user I should be able to signup for a paid recurring account – 8
  3. As a user I should be able to make connections with other users via Facebook – 13
  4. As a user I should be able to send a rocket to the moon – !

When the other stories become much more complex, the CRUD task is again shown to be significantly different in complexity, this time in the other direction. In this group of stories its hard to imagine that managing portfolio photos is only two orders of magnitude away from a recurring payment e-commerce system.

Relative Complexity Works

Its important to estimate a group of stories so that the complexity of each story is relative to the next. In Mike Cohn’s Agile Estimating and Planning, he describes a method of estimating stories called “Analogy”.

When estimating by analogy, the estimator compares the story being estimated with one or more other stories. If the story is twice the size, it is given an estimate twice as large.

When you apply this technique to your estimation process, you will have a more coherent set of estimates. From this you will be more likely to determine an accurate estimated velocity and you will have a better overall sense for the scope of the project.

Hurdles to Estimating by Analogy

  • Estimating all of your stories one by one makes it difficult to estimate a relative complexity as you only have the previously estimated stories with which to compare your current story. A group of especially simple or complex stories could be waiting towards the end of the sessions.
  • Estimators who are new to the agile estimation process might have difficultly estimating a story without a point of reference.
  • Estimating with a baseline can be a difficult habit to break since it provides a convenience and familiarity to the estimator.
  • Two seemingly similar projects may have a greater than expected difference in total number of story points, even though their velocities are relatively the same.

{ 2 comments }

The Secret to Awesome Agile Development

December 28, 2009

With a little hard work and my secret development ingredient, you can be a better Agile Developer

Recently my fellow developers at Integrum and I took a survey that helped us assess our team with regard to our Agile practices. When taking the survey, and now reviewing it later on, I was struck by how many [...]

Read the full article →

The 7 Bullshit Agile Estimation Problems

December 3, 2009

Estimating stories for an upcoming project is one of the more difficult tasks that agile teams have to perform. It’s never easy to determine how difficult it will be to implement a particular feature, especially when you’ve got different personalities, goals, and levels or experience in the same room. Unfortunately this all leads to people [...]

Read the full article →

Intel Developer Ignite #2

November 17, 2009

I had a blast presenting at the recent Intel Developer Ignite. In my quick five minute presentation I mapped ten of Aesop’s fables to modern day software engineering challenges and principles. If you missed the event, or just want to check out my presentation again, here it is!

Age-Old Solutions to Everyday Problems

Big thanks to Intel [...]

Read the full article →

The Secret to Full Stack Testing with Cucumber and Webrat

November 7, 2009

Today I gave a presentation at Desert Code Camp about BDD, Cucumber, Webrat and User Stories. If you’d like to find review the slides or download the code I used during the presentation here they are.

Read the full article →

Cucumber Table Transformations with Factory Girl Sequences

November 6, 2009

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.

Read the full article →

Missing host to link to! Please provide :host parameter

November 5, 2009

If you’ve followed my version of the authlogic account activation tutorial or the original version by Matt Hooks you might have run into this error

Read the full article →

Does the Chronic Time Parsing Library Break with DST Changes?

November 3, 2009

I’ve been troubleshooting a problem on an existing application for the last week or so that deals with the parsing of dates using the Chronic time parsing library. Today the problem magically solved itself, without me doing anything. These types of self solving problems are usually more frustrating than problems you can’t solve at all, so I took a little extra time to experiment with the particular date format I was using and found what might be a problem with the Chronic library when it gets to the “fall back” DST change in the fall.

Read the full article →

Make Fun of Your Client To Prevent Defects

October 27, 2009

One thing I’ve always heard about learning a new language is that you can’t consider yourself fully fluent until you can tell a joke. Telling a joke requires the understanding of homonyms and how words flow together. “So a guy walks into a bar…” sounds different than “A man enters beverage store…”.

When dealing with clients, it’s easy to joke about your client, but don’t miss an opportunity to prevent future missteps once you know more about them.

Read the full article →