Sharing data between bindings in Specflow

When performing Acceptance tests, you sometimes need to pass data between the individual steps within a scenario. In other words, data needs to be exchanged between different bindings during the execution of the test.
SpecFlow provides several ways of sharing data between bindings.

  • If all our step definitions are within the same binding class, introduce a field to this step definition class and reference it whenever it is required to be used in the same class.
  • Another alternative is to use the SpecFlow ScenarioContext, this gives you access to a dictionary where you can store the data you want to pass across different steps.
  • The third option is that SpecFlow provides a simple dependency injection known as “context injection”. Using this you can share state between step definitions even if they're in different Binding classes.

Let’s talk about third option in more detail. With this simple dependency injection you can group the shared state to context-classes, and inject them into every binding class that is interested in that shared state.
To use context injection, you need to do the following.

  • Create POCO classes representing the shared data, In other words define a context to hold data you would like to share.
public class UserLoginDetails
{
    public string Email { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Domain { get; set; }
}
  • Define it as constructor parameters in every binding class you need the context data & Save the constructor argument to an instance field, so you can use it in the step definition. This has been explained in the example below
  • Set context in the Setup or any other step during test life cycle as shown below.
public class LoginSetupSteps
{
	private readonly UserLoginDetails _userLoginDetails;
	public LoginSetupSteps (UserLoginDetails userLogin)
	{
		_userLoginDetails = userLogin;
	}
	[Given(@"User has Logged in with UserName '(.*)'")]
	public void GivenUserHasLoggedInWithUserName (string userName)
	{
		_userLoginDetails.UserName = userName;
	}
}
  • Use the context whenever you need during test execution as shown below.
public class LoginVerificationStpes
{
	private readonly UserLoginDetails _userLoginDetailsToBeVerified;
	public LoginVerificationStpes(UserLoginDetails loginDetails)
	{
		_userLoginDetailsToBeVerified = loginDetails;
	}
	[Then(@";user should be allowed to use MyWorkNow")]
	public void ThenUserShouldBeAllowedToUseMyWorkNow()
	{
		AssertThatUserIsAllowedToUseMyWorkNow(_userLoginDetailsToBeVerified);
	}
}

The following caveats should be kept in mind when using context injection.

  • Constructors must be public for SpecFlow to see them
  • If the step definition class has more than one public constructor then SpecFlow takes the first one it finds
  • The dependencies are resolved recursively until they are all satisfied
  • The lifetime of these objects is limited to a single scenario execution
  • If any dependencies implement IDisposable they will be disposed after the end of the scenario execution