BDD in .NET with Cucumber part 2: Making scenarios easier to read with tables

The BDD Given/When/Then scenario structure is great for workflows, but too verbose when a specification includes lots of similar cases or when complicated objects come into play. This is where Cucumber tables come in. Tables provide just enough structure to group related data together so that it can be efficiently manipulated and easy to understand in a scenario. After setting up Cucumber for .NET and going through a basic example, it’s time to learn how to use tables to make complex scenarios easier to read and manage.

Handling lists of objects

In addition to matching regular expressions, Cucumber steps can also receive tables. Cucumber uses the standard wiki syntax for tables, with cells separated by the pipe symbol. Instead of a single greeting which we’ve described in part 1 of this tutorial, let’s create a scenario for a group of greetings. Add this to your feature file:

	Scenario: Hello World with tables
		Given the mailing list is
			|action|subject|
			|Hello|Mike|
			|Jump|Tom|		
		When the list is processed
		Then the following messages have gone out
			|message|
			|Hello, Mike|
			|Jump, Tom|


The table API

Running Cuke4Nuke without the -q flag will tell us which missing methods it expected to find (in the You can implement step definitions for undefined steps with these snippets section):

[Pending]
[Given(@"^the mailing list is$")]
public void TheMailingListIs(Table table)
{
}

[Pending]
[When(@"^the list is processed$")]
public void TheListIsProcessed()
{
}

[Pending]
[Then(@"^the following messages have gone out$")]
public void TheFollowingMessagesHaveGoneOut(Table table)
{
}


As you can see from the snippets that Cuke4Nuke generated, tables in scenarios are passed as Cuke4Nuke.Framework.Table objects. This class has three interesting members:

  • The first is is the Data field which is a list of lists of strings. Effectively, this represents the table, row by row, including the header. The first table from the scenario example would be represented by the following list: [ ["action","subject"], ["Hello","Mike"],["Jump","Tom"] ]
  • The second is the Hashes() method which transforms the data table into a list of Dictionary objects, creating key-value pairs from data rows (below the header) by reading keys from the header row. The first table from the scenario example would be represented by the following Dictionary: [ [“action”=>”Hello”,”subject”=>”Mike”], [“action”=>”Jump”,”subject”=>”Tom”] ]. The Hashes() method makes it a bit easier to process raw table data.
  • The third is the AssertSameAs(Table) method which is a shorthand for comparing two tables. This will come in handy for checking data in batches (eg in the TheFollowingMessagesHaveGoneOut method)

Copy and paste the snippets generated by Cuke4Nuke into your step definition class, remove the Pending attributes, and let’s add some meat to it. Again, on a real project the steps would talk to our domain classes but let’s keep it simple now to demonstrate Cucumber features. The TheMailingListIs method will convert the input table to a list of our domain Message objects, TheListIsProcessed will generate string contents for each message and store it in an outgoing list, and the TheFollowingMessagesHaveGoneOut method will create a Cucumber table from that list and compare it to the list of expected values, defined in the scenario. Notice how we use the Hashes() method to process an input table and the Data property to create an output table.

class Message 
{ 
    public String Action { get; set;} 
    public String Subject {get; set; }
}

List messages = new List();

[Given(@"^the mailing list is$")]
public void TheMailingListIs(Table table)
{
    foreach (Dictionary element in table.Hashes())
    {
        messages.Add(
          new Message { Action = element["action"], 
                               Subject = element["subject"] });
    }
}
List outgoing = new List();
[When(@"^the list is processed$")]
public void TheListIsProcessed()
{
    foreach (Message m in messages)
    {
        outgoing.Add(m.Action + ", " + m.Subject);
    }          
}

private List list(String s)
{
    List l = new List();
    l.Add(s);
    return l;
}

[Then(@"^the following messages have gone out$")]
public void TheFollowingMessagesHaveGoneOut(Table table)
{
    Table expected = new Table();
    expected.Data.Add(list("message"));
    foreach (String s in outgoing)
    {
        expected.Data.Add(list(s)); 
    }
    table.AssertSameAs(expected);
}

Now you re-run Cuke4Nuke and should see the tables in a green colour, meaning that the tests passed:

There it is. That’s all you need to know about using tables with Cucumber and Cuke4Nuke. Rinse and repeat until happy. If you’re interested in learning more about Behaviour-Driven Development and Cucumber, check out my new workshops.

I'm Gojko Adzic, author of Impact Mapping and Specification by Example. I'm currently working on 50 Quick Ideas to Improve Your User Stories. To learn about discounts on my books, conferences and workshops, sign up for Impact or follow me on Twitter. Join me at these conferences and workshops:

Specification by Example Workshops

Product Owner Survival Camp

Conference talks and workshops

One thought on “BDD in .NET with Cucumber part 2: Making scenarios easier to read with tables

  1. Pingback: How to Remove Duplication in Cucumber Tests Using Scenario Outlines | Richard Lawrence

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>