SpecFlow: table.CreateInstance<> only loads a shallow model, table.CreateDeepInstance<> to the rescue

Hackered
Tuesday, June 9, 2015
by Sean McAlinden

CreateInstance is a massively useful extension within SpecFlow, it allows you to effortlessly instantiate a class populated with the values from a Gherkin table on your feature file.

Whilst this is undoubtedly awesome, it can only handle simple and primitive types so any complex properties are ignored.

A recent set of acceptance tests I have worked on required a deeper set of JSON structures to be persisted,  in order to fulfil this my initial main choices were:

  • Generate the JSON documents and add them to the acceptance test project
  • Generate the structures within a SpecFlow Hook
  • Create some nested structure within a SpecFlow Table, parse and build the structured documents

A key benefit of using SpecFlow is the Gherlin syntax, it allows the business to read and understand the acceptance tests, if I were to create the structures using any of the above approaches the test data is generated behind the scenes, therefore the business cannot see the full picture.

 It would be great if table.CreateInstance<> could instantiate a deep object graph

This would be ideal, it would mean the test data object graphs could be created from a Gherkin table, allowing the business to fully understand the assertions.

Unfortunately, table.CreateInstance<> only instantiates simple type properties so after hunting around a while to no avail, I realised it was time for a little reflection... well actually a whole load of C# reflection to create the answer...

table.CreateDeepInstance<> to the rescue

I have created a new table extension called CreateDeepInstance, if you NuGet Embarr.SpecFlow this method will appear next to CreateInstance and CreateSet in your intellisense.

It allows you to specify a deeper object graph within your gherkin table using normal dot syntax for example:

Scenario: Create deep model successfully
	When the following deep properties are hydrated:
	| property                   | value          |
	| Name                       | My Index 1     |
	| Dec                        | 1.2            |
	| NullDec                    | 1.2            |
	| Doub                       | 0.8            |
	| Child.Dec                  | 3.2            |
	| Coll[0].Name               | My Coll 0 Name |
	| Coll[0].Dec                | 3.4            |
	| Coll[1].Name               | My Coll 1 Name |
	| Coll[1].Dec                | 4.5            |
	| Coll[1].Coll[0].Dec        | 10.99          |
	| Child.Coll[0].Dec          | 11.99          |
	| Child.Coll[0].Coll[0].Name | V.Deep         |

As you can see, collection indexers and complex types are supported.

By calling:

[When(@"the following deep properties are hydrated:")]
public void WhenTheFollowingDeepPropertiesAreHydrated(Table table)
{
    var deepTable = table.CreateDeepInstance<DeepInstanceModel>();
}

A populated instance of the following class is created:

public class DeepInstanceModel
{
    public string Name { get; set; }

    public decimal Dec { get; set; }

    public decimal? NullDec { get; set; }

    public double Doub { get; set; }

    public List<DeepInstanceModel> Coll { get; set; }

    public DeepInstanceModel Child { get; set; }
}

I hope this is useful, I'll carry on adding to this library as and when I have some useful new code.

Please check it out at:https://bitbucket.org/embarr-development/embarr.specflow

And download it via NuGet: https://www.nuget.org/packages/Embarr.SpecFlow