Specflow Step Definitions Explained

By | April 22, 2016

This is c# based Specflow Step Definitions tutorial will explore intricacies of step definitions. This post follows the beginner tutorial I wrote before. I will recommend that you go through that before reading this post. Step definitions look simple but they have lot of hidden power. So without wasting any more time, let us as usual go for the WHY followed by HOW.

WHY

We humans are greedy. We always want more. In case of Specflow step definition, users were quick to ask

  • Can we pass data from specflow step definition to background implementation. (Hint: Yes)
  • Can we share data between the implementations of specflow step definitions. (Hint: Yes)
  • Can we pass a table of data from specflow step definition to background implementation. (Hint: Yes)
  • Can we keep implementations in some other file and make step definitions use them instead. (Hint: Yes (TODO))
  • Can we share data between such implementations. (Hint: Yes (TODO))
  • What else I can do with specflow step definition? (Check this (TODO))
  • Was “Batman v Superman : Dawn of Justice” a good idea. (Definitive answer: NO, Leave Batman alone.)

HOW

So let us start tackling each of these questions one at a time.

Pass data from specflow step definition to background implementation

First things first. I will use the same project I used in the beginner tutorial. This time I will add another file called BatmobileControl.cs. This will work as system under test. These are the contents

Let us add a feature file called Batmobile.feature. If you have gone through the beginner post, then you can understand the contents.

Here I am passing parameters from the step definition to the background implementation. See the double quotes around word stealth? That means I am telling specflow to interpret it as a string parameter and generate the function signature of the background implementation accordingly. The number 40 will automatically be interpreted by specflow as an integer parameter.
Let us generate the step definitions. I will set style as regular expression in the attributes.
Here is the step file BatmobileSteps.cs. I have added the implementation of the steps to save time.

Check out the signature for methods GivenISetItToDriveOnModeForMins and ThenBatmobileDrivesOnModeForMins. Specflow by default names the parameters p0, p1 and so on. I changed them to something more readable. The parameter types are of interest to us. See how specflow correctly assigned them as string and integer type.

Let us run this in debug mode to see if the data does get passed.
Specflow passing data to step definitions
And the test passes.
Specflow passing data to step definitions
We just passed data to the step definition implementation. If you want even more details then go to Deep Dive post (coming soon). Moving on to the next.

Share data between the implementations of specflow step definitions

We have already seen one way of sharing data between the Specflow Step Definitions. See the file BatmobileSteps.cs. We maintain a private instance of BatmobileControl class. We populate it in the GivenIHaveStartedBatmobile() function. The rest of the functions then happily use this instance to do their work. However there is one major issue here. It is very much possible to have Specflow Step Definitions exist in different classes. Then this trick will not work.

Enter ScenarioContext.

As the name tells, this is a way to share data between the steps of a “Given Scenario”. Not between scenarios. Now with that thing cleared let us change the feature file to show the impact this has on the scenario.

Check the last step. We need not pass data to it again. It will use data which was stored by the step preceding it in the implementation file. As shown below in BatmobileSteps.cs

See how we add the data we want to store into the ScenarioContext in form of key value pair in method GivenISetItToDriveOnModeForMins(). In the method ThenBatmobileDrivesAccordingly() we extract this data. The only downside is the casting which we have to do.

Let us debug to see the data we are getting.
Specflow scenario context in action
And the test passes.
Specflow scenario context in action

What if you are not happy with the cast operations needed for ScenarioContext. There is a better way of doing this sharing of data between Specflow Step Definitions.

Enter context injection.

It runs on the dependency injection support which Specflow provides. We have to create an class which will contain the data to be shared between the steps of a given scenario. Then we inject this class into the constructor of the step definition class. Then it is available to all the step definition implementations. “Talk is cheap. Show me code”. Let us refactor the sample we have to check this feature.

I will add first the class which will have the necessary wiring to store the data inside. This data will be later shared between the steps. Here is the class which I named as BatmobileControl.cs.

I will just change the BatmobileSteps.cs to show this cool feature in action.

Important lines:

  • Line 11: We have a constructor for the class containing the step definitions here. Constructor injection in action. Check my post on dependency injection for more details.
  • Line 13: Dependency framework in Specflow instantiates an object of BatmanmobileControl type and passes it to constructor. See my post on Unity for more details.
  • Line 19: No longer need to new up an instance of BatmanmobileControl type!!
  • Line 25, 26: Saving the data passed into the BatmanmobileControl instance.
  • Line 33, 34: Accessing the data saved in BatmanmobileControl instance. No casting needed. Woo hoo.

You can check my post on dependency injection and Unity to get some background on dependency injection if needed.
So does it work?

SpecflowContextInjection

This works even if you split the step definitions between two separate classes. Just make sure that each class has got a constructor which is taking an instance of the class which contains the common data which you want to share between the steps.

One thing to remember is that this object lifespan is limited to the time the scenario is executing. Next scenario will gets a new instance of this object. So you cannot pass data between the scenarios.

Pass a table of data from specflow step definition to background implementation

To pass data from Specflow Step Definitions the feature file Batmobile.feature changes a little bit. Here it is.

As highlighted lines show, the table is specflow world is not pretty. But it gets the job done. When you generate the background implementation you will see that the generated function will be taking a parameter of type Table which is provided by Specflow. Then you can chomp your way through the table to get the data you want. Here is the background implementation file BatmobileSteps.cs.

As you can see in the highlighted text, the implementation now takes a parameter of type table. We add the function InitializeDriveModes to BatmobileControl.cs.

You can see in InitializeDriveModes that we can access the individual rows. The indexing is zero based. You can LINQ your way through this but I will keep things real simple. There are many operations which the Table supports. Do have a look. I changed SetDriveMode so that now it checks whether the drive mode is supported. If not then it sets the OperationFailed flag to true.
I have set up the feature file such that it tries to set the Batmobile into a mode for more than supported duration. I expect a failure as seen in the last step of the feature file. In the implementation of that step, I check the OpreationFailed flag. So does the test pass? Indeed it does.

Leave a Reply