Skip to main content
Version: shouty

Data tables

If you look at the Multiple shouters scenario that includes Sean, Lucy, and Oscar you can see that it’s a bit hard to read. SpecFlow and Gherkin let you use data tables in a step, which can help you remove clutter and improve the readability of the specification. The scenario could be simplified by using a data table, like this:

 Given people are located at
| Name | X | Y |
| Lucy | 0 | 0 |
| Sean | 0 | 500 |
| Oscar | 1100 | 0 |

Modify the scenario and run SpecFlow. You should get an undefined step error, because this is a new step. Look at the snippet that has been emitted, which defines a step definition that takes a parameter .

There are a number of ways that you can use this parameter, but to start with let’s keep it simple and use the Table.Rows property, which returns a collection of objects that implement the IDictionary<string, string> interface. There’s an entry in the collection for each row (except the heading).

You can use this in your step definition to see an example.

foreach (var row in table.Rows)
{
// access cells by e.g. row["name"]
}
discuss

How much do you like the look of the code?

Next, create a new class PersonLocation (in the Shouty.Specs project) with public properties called Name, X, and Y.

Then, change the step definition parameter from Table to PersonLocation[].

When you run SpecFlow you should get an InvalidCastException exception because SpecFlow doesn’t know how to convert the data table to a PersonLocation array. Add the following to the ShoutStepDefinitions class:

ShoutStepDefinitions.cs
// ...

[StepArgumentTransformation]
public PersonLocation[] ConvertPersonLocations(Table personLocationsTable)
{
return personLocationsTable.Rows
.Select(row => new PersonLocation
{
Name = row["name"],
X = int.Parse(row["x"]),
Y = int.Parse(row["y"])
}).ToArray();
}

Change the sample code above to iterate over the list.

discuss

Is the code more readable?

Now get the scenario passing again.

The "Assist" table helper functions

The Rows property is very useful, but included in the TechTalk.SpecFlow.Assist namespace are some extension methods of Table that provide some further functionality. The SpecFlow.Assist helpers docs are helpful here.

  • CreateSet<T>() — This method will create an instance of T for each non-header row of the table. It will try to match the row headers with the names of public properties of T and set them using the values found in each row.
  • CreateInstance<T>() — Works similarly to CreateSet, but creates a single instance from data tables that have either a single data row or field and value columns and a row for each field setting.
  • CompareToSet<T>(IEnumerable<T>, bool) and CompareToInstance<T>(T) — These helper methods can be used in Then steps to assert that a list of T objects contain the property values that were specified in the data table.

Change your implementation of the ConvertPersonLocations method to use the CreateSet method. Don’t forget to add a namespace using to the TechTalk.SpecFlow.Assist namespace.