SpecFlow Assist Helpers

To use these helpers, you need to add the TechTalk.SpecFlow.Assist namespace to the top of your file:

using TechTalk.SpecFlow.Assist;

CreateInstance

CreateInstance<T> is an extension method of Table that will convert the table data to an object. For example, if you list data in a table that lists the values of your object like this:

Given I entered the following data into the new account form:
| Field              | Value      |
| Name               | John Galt  |
| Birthdate          | 2/2/1902   |
| HeightInInches     | 72         |
| BankAccountBalance | 1234.56    |

... or in a horizontal table like this:

Given I entered the following data into the new account form:
| Name      | Birthdate | HeightInInches | BankAccountBalance |
| John Galt | 2/2/1902  | 72             | 1234.56            |

... you can convert the data in the table to an instance of an object like this:

[Given(@"Given I entered the following data into the new account form:")]
public void x(Table table)
{
    var account = table.CreateInstance<Account>();
    // account.Name will equal "John Galt", HeightInInches will equal 72, etc.
}

The CreateInstance<T> method will create the Account object and set properties according to what can be read from the table. It also uses the appropriate casting or conversion to turn your string into the appropriate type.

The headers in this table can be anything you want, e.g. "Field" and "Value". What matters is that the first column contains the property name and the second column the value.

Alternatively you can use ValueTuples and destructuring:

[Given(@"Given I entered the following data into the new account form:")]
public void x(Table table)
{
    var account = table.CreateInstance<Account>();
    var account = table.CreateInstance<(string name, DateTime birthDate, int heightInInches, decimal bankAccountBalance)>();
    // account.name will equal "John Galt", heightInInches will equal 72, etc.
}

This feature is supported from version 2.2.1.

Important: In the case of tuples, you need to have the same number of parameters and types; parameter names do not matter, as ValueTuples do not hold parameter names at runtime using reflection. Scenarios with more than 7 properties are not currently supported, and you will receive an exception if you try to map more properties.

CreateSet

CreateSet<T> is an extension method of Table that converts table data into a set of objects. For example, assume you have the following step:

Given these products exist
| Sku              | Name             | Price |
| BOOK1            | Atlas Shrugged   | 25.04 |
| BOOK2            | The Fountainhead | 20.15 |

You can convert the data in the table to a set of objects like this:

[Given(@"Given these products exist")]
public void x(Table table)
{
    var products = table.CreateSet<Product>();
    // ...
}

The CreateSet<T> method returns an IEnumerable<T> based on the matching data in the table. It contains the values for each object, making appropriate type conversions from string to the related property.

CompareToInstance

CompareToInstance<T> makes it easy to compare the properties of an object against a table. For example, you have a class like this:

public class Person
{
    public string FirstName { get; set;}  
    public string LastName { get; set; }
    public int YearsOld { get; set; }
}

You want to compare it to a table in a step like this:

Then the person should have the following values
| Field     | Value |
| FirstName | John  |
| LastName  | Galt  |
| YearsOld  | 54    |

You can assert that the properties match with this simple step definition:

[Then("the person should have the following values")]
public void x(Table table){
    // you don't have to get person this way, this is just for demo
    var person = ScenarioContext.Current.Get<Person>();

    table.CompareToInstance<Person>(person);
}

If FirstName is not "John", LastName is not "Galt", or YearsOld is not 54, a descriptive error showing the differences is thrown.

If the values match, no exception is thrown, and SpecFlow continues to process your scenario.

CompareToSet

CompareToSet<T> makes it easy to compare the values in a table to a set of objects. For example, you have a class like this:

public class Account
{
    public string Id { get; set;}
    public string FirstName { get; set;}
    public string LastName { get; set;}
    public string MiddleName { get; set;}
}

You want to test that your system returns a specific set of accounts, like this:

Then I get back the following accounts
| Id     | FirstName | LastName |
| 1      | John      | Galt     |
| 2      | Howard    | Roark    |

You can test you results with one call to CompareToSet:

[Then("I get back the following accounts")]
public void x(Table table){
    var accounts = ScenarioContext.Current.Get<IEnumerable<Account>>();

    table.CompareToSet<Account>(accounts)
}

In this example, CompareToSet<T> checks that two accounts are returned, and only tests the properties you defined in the table. It does not test the order of the objects, only that one was found that matches. If no record matching the properties in your table is found, an exception is thrown that includes the row number(s) that do not match up.

Column naming

The SpecFlow Assist helpers use the values in your table to determine what properties to set in your object. However, the names of the columns do not need to match exactly - whitespace and casing is ignored. For example, the following two tables are treated as identical:

| FirstName | LastName | DateOfBirth | HappinessRating |
| First name | Last name | Date of birth | HAPPINESS rating |

This allows you to make your tables more readable to others.

Aliasing

(Note: Available with version 2.3 and later)

If you have properties in your objects that are known by different terms within the business domain, these can be Aliased in your model by applying the attribute TableAliases. This attribute takes a collection of aliases as regular expressions that can be used to refer to the property in question.

For example, if you have an object representing an Employee, you might want to alias the Surname property:

    public class Employee
    {
        public string FirstName { get; set; }
        public string MiddleName { get; set; }

        [TableAliases("Last[]?Name", "Family[]?Name")]
        public string Surname { get; set; }
    }

Test writers can then refer to this property as Surname, Last Name, Lastname, Family Name or FamilyName, and it will still be mapped to the correct column.

The TableAliases attribute can be applied to a field, a property as a single attribute with multiple regular expressions, or as multiple attributes, depending on your preference.

Extensions

Out-of-the-box, the SpecFlow table helpers knows how to handle most C# base types. Types like String, Bool, Enum, Int, Decimal, DateTime, etc. are all covered. The covered types can be found here. If you want to cover more types, including your own custom types, you can do so by registering your own instances of IValueRetriever and IValueComparer.

For example, you have a complex object like this:

    public class Shirt
    {
        public string Name { get; set; }
        public Color Color { get; set; }
    }

You have a table like this:

| Name | Color |
| XL   | Blue  |
| L    | Red   |

If you want to map Blue and Red to the appropriate instance of the Color class, you need to create an instance of IValueRetriever that can convert the strings to the Color instance.

You can register your special IValueRetriever (and/or an instance of IValueComparer if you want to compare colors) like this:

[Binding]
public static class Hooks1
{
    [BeforeTestRun]
    public static void BeforeScenario()
    {
        Service.Instance.RegisterValueRetriever(new ColorValueRetriever());
        Service.Instance.RegisterValueComparer(new ColorValueComparer());
    }
}

Examples on implementing these interfaces can be found as follows: