SpecFlow

Extending SpecFlow functionality

What is SpecFlow

SpecFlow is an excellent framework for BDD implemented in .NET. It has variety of out-of-the-box functionality: - Gherkin support - IDE integration - Test runners - LivingDoc - And much more You can read more about this excellent tool at https://specflow.org

Extension points

SpecFlow allows injecting different kinds of behavior into the regular test/scenario flow, most usually by defining one ore more hooks. You can see some examples at their docs: https://docs.specflow.org/projects/specflow/en/latest/Bindings/Hooks.html Additionally each scenario has its own context during execution allowing to run the tests in parallel and register custom dependencies into the provided DI container. This allows us to add our own lifecycle functionality before and after each scenario.

Lifecycle Hook

The main extension point to the whole lifecycle which allows injecting functionality uses SpecFlow's Hooks mechanism and is invoked during scenario initialization. See LifecycleHook class below. Pay attention to two additional extension points within the class. They allow us to provide our custom initialization, mostly for the inversion-of-control container.

Two additional configuration points include Bootstrapperwhich is responsible for the common initialization and also allows us to use our project's custom namespace, and Startup that is responsible for bootstrapper creation and exposes an extension point for additional configuration

using Attest.Fake.Data;
using Attest.Testing.SpecFlow;
using BoDi;
using Solid.Practices.IoC;
using TechTalk.SpecFlow;

[Binding]
internal sealed class LifecycleHook : LifecycleHookBase
{
     public LifecycleHook(ObjectContainer objectContainer)             
       :base(objectContainer)        
     {        
     }
     
     protected override void InitializeContainer(IIocContainer iocContainer)
     {
       iocContainer.Initialize();
     }
     
     protected override void BeforeScenarioOverride(IIocContainer iocContainer)
     {
        base.BeforeScenarioOverride(iocContainer);
        BuildersCollectionStorageContext.Current = iocContainer.Resolve<IDataStorage<string>>();
     }
}

Scenario Data Store

SpecFlow provides out-of-the-box facilities for storing the data that's available during scenario execution. They are available using ScenarioContext class which can be injected into the respective step or step container (class). However there are cases where we would like to interact with exact properties and employ the full power of static language like C# to make sure we get and set the right data. This becomes possible using the following approach:

using Attest.Testing.SpecFlow;
using Solid.Practices.IoC;
using TechTalk.SpecFlow;

internal sealed class BootstrapperScenarioDataStore : ScenarioDataStoreBase 
{ 
    public BootstrapperScenarioDataStore(ScenarioContext scenarioContext) 
    : base(scenarioContext) 
    { }
    
    public IDependencyRegistrator Container
    {
        get => GetValue<IDependencyRegistrator>();
        set => SetValue(value);
    }

    public FakeBootstrapper Bootstrapper
    {
        get => GetValue<FakeBootstrapper>();
        set => SetValue(value);
    }
}

The BootstrapperScenarioDataStore class is then injected into the respective steps and ensure clearer API for the developers.

Last updated