Konzy262
Konzy262

Reputation: 3087

How to share step binding class in SpecFlow with context injection

I'm using context injection in SpecFlow but I've encountered an issue whereby I want to share a step bindings class, but I don't necessarily need to register all the constructor parameters.

I have various BeforeScenario hooks in a separate binding/hooks class where I register objects with the container, like below....

[Scope(Scenario = "Scenario A")]
[BeforeScenario(Order = 5)]
public void CreateApprenticeWithChecklist()
{
    //Other stuff

    _container.RegisterInstanceAs<Apprentice>(apprentice);
}

[Scope(Scenario = "Scenario B")]
[BeforeScenario(Order = 5)]
public void CreateApprenticeWithChecklistAllSignOff()
{
    //Other stuff

    _container.RegisterInstanceAs<Apprentice>(apprentice);
    _container.RegisterInstanceAs<Dictionary<string, UserDetails>>(new Dictionary<string, UserDetails>
    {
        ["Employer"] = employer.Key,
        ["Training Provider"] = trainingProvider.Key
    });
}

I then have a step definitions class that both these scenarios will use....

[Binding]
public sealed class ApprenticeChecklistSteps
{
    private Apprentice _apprentice;
    private UserDetails _employer;
    private UserDetails _trainingProvider;   

    public ApprenticeChecklistSteps(Apprentice apprentice, Dictionary<string, UserDetails> userDetails)
    {
        _apprentice = apprentice;
        _employer = userDetails["Employer"];
        _trainingProvider = userDetails["Training Provider"];
    }

    //etc

The problem is if I'm running Scenario A there will be no registration for Dictionary<string, UserDetails> userDetails and so I will get the Multiple public constructors with same maximum parameter count are not supported! error.

How can I share step bindings between scenario's if I don't necessarily have or need a certain constructor parameter?

The only solutions I see are hacky

Create 'blank' registrations for all the unused constructor parameters so they are initialized

Or...

Create a separate POCO that contains all the possible registrations, and register whatever I need, when I need it

public class CheckListContext
{
    public Apprentice Apprentice { get; set; }
    public Dictionary<string, UserDetails> UserDetails { get; set; }
}

The registration in Scenario A BeforeScenario step would then become...

_container.RegisterInstanceAs<CheckListContext>(new CheckListContext{Apprentice = apprentice});

Upvotes: 1

Views: 816

Answers (1)

Greg Burghardt
Greg Burghardt

Reputation: 18783

It looks like you just want to share data between steps, not step bindings. The way to do this in SpecFlow is by using the scenario context to set data in one step and retrieve it in another step. For instance, I would have a step called Given an apprenticeship exists. The step definition for this would be something like:

[Binding]
public class ApprenticeshipSteps
{
    private readonly ScenarioContext scenario;

    public ApprenticeshipSteps(ScenarioContext scenario)
    {
        this.scenario = scenario;
    }

    [Given(@"an apprenticeship exists")]
    public void GivenAnApprenticeshipExists()
    {
        var apprenticeship = new Apprenticeship(...);

        scenario["apprenticeship"] = apprenticeship;

        // save to database if need be
    }
}

[Binding]
public class MoreStepDefinitions
{

    private readonly ScenarioContext scenario;

    public MoreStepDefinitions(ScenarioContext scenario)
    {
        this.scenario = scenario;
    }

    [Then(@"a step that needs the apprenticeship")]
    public void AnotherStep()
    {
        var apprenticeship = (Apprenticeship)scenario["apprenticeship"];
    }
}

If need be you can put the apprenticeship Id in the scenario context instead if you need to get it from the database. The scenario context is basically just a bag of data passed around from step definition to step definition. This is the preferred way to share data between steps in SpecFlow.

Upvotes: 1

Related Questions