I didn’t realise that this existed and is very useful. This is another good way to skin the same cat.

This is a great solution and will probably work for you. The only small downside is that because it is casting under the covers you have to provide the type that you are expecting/setting through the generic argument. This is probably not a problem but makes the code a little verbose.

The nuget package moves the casting further up the pipeline into the interceptor so you don’t have to worry about it. By the time that you come to use the context the objects have already been cast into your interface type. The downside to the nuget package however is that you have to derive you step definition classes from an abstract base class BaseBinding. In some circumstances this may not be possible and is a nuisance. The reason I had to do it this way is because it’s the only way that I could hook into the object creation pipeline. If there is a better way then please let me know.

Whilst we are on the subject of the StronglyTypedContext I’d like to take this opportunity to point out one more test case for the shared context. It works across multiple step definition classes as can be seen from the code snippet below:

I write a lot of BDD tests day to day using SpecFlow. I really love SpecFlow as a tool, as it allows you to write clear acceptance tests that are driven by business criteria and written in a language that the business can understand.

One of the things I dislike about SpecFlow however is the way the ScenarioContext is implemented. The ScenarioContext is a dictionary that is passed to each step in your test by the SpecFlow framework. It is a place where you can store test data between steps. The trouble is the signature to get and set an item is as follows:

The trouble with this is that when people are not disciplined you end up with magic strings everywhere, scattered throughout all of your step definition files. You also lose your compiler as a safety guard. For example:

[Binding]
public class Steps
{
[Given("I have done something")]
public GivenIHaveDoneSomething()
{
ScenarioContext.Add("customerId", 1234);
}
// then further down
[When("I do this action")]
public WhenIDoThisAction()
{
// This line will throw a NullReferenceException
var customerId = (int)ScenarioContext.Current["wrongKey"];
}
}

The two problems that I mention are highlighted above. Firstly you have to remember the magic string that you used in the ScenarioContext. You also have to know the type to cast it. The above code will compile but will throw an error at runtime. Runtime errors are much harder to catch than compile time errors. Where possible it’s best to make the compiler work for you.

To aid with that I have written a nuget package called StronglyTypedContext. This nuget package provides you with the ability to have strongly typed scenario contexts alleviating both of the problems above.

Look how much cleaner the code above is. No casting is necessary and we still have access to our ScenarioContext variables throughout the test. The heavy lifting for this is done using a constructor the base class (BindingBase) which uses Castle Dynamic Proxy at runtime. Note how in the code above nowhere do we actually implement the IContext interface?

So how does this work?

1) The abstract class BindingBase has a constructor that looks for public virtual properties marked with ScenarioContextAttribute

2) Castle Dynamic Proxy is then used to generate a proxy implementation for each property and sets each property to the created proxy. The proxy is created with a custom interceptor set.

3) The custom interceptor is invoked when you call get or set on the public virtual property. For example in the code snippet above when you call Context.CustomerId you are actually invoking CustomerId on the proxy which gives the interceptor a chance to get involved.

4) The interceptor then checks to see if the property is being get or set. It then either retrieves or stores the value in the SpecFlow Scenario context based on a key that is generated from a combination of the type and property name.

I wrote the whole library using a TDD approach. You can see the full source code on the github page. If you are interested in digging in more detail I would suggest you start by looking at the ProxyInterceptor class. This is where the magic happens.