In the previous chapter, we discussed the Automation phase, which is the final of the three phases in the textbook BDD process. We were also introduced to Reqnroll as a tool that supports the BDD process by converting the business requirements, written in Gherkin, into executable C# code. This means that we can use Reqnroll to drive our automated acceptance tests right from our specifications. Finally, we saw how to get started with using Reqnroll and create a new Reqnroll project using the Visual Studio Reqnroll extension.
In this chapter, we are going to take the Gherkin specifications for our loan application feature that we created in Chapter 3 on Formulation and convert them into working and running C# code that will ultimately drive our automated acceptance tests for this feature.
The first step to do this is to add a new feature file to our project. This feature file will contain the specifications for our loan application feature, written in Gherkin. We do this by right-clicking on the Features folder and choosing to add a new item. Since we've installed the Reqnroll extension for Visual Studio, feature files are a recognized item format and we can use that. All we need to do now is give the feature file a meaningful name, so let's go with LoanApplication.
We'll immediately replace the default contents of this new feature file with our own specifications. As you can see, the scenarios and their corresponding examples for all three rules that make up the business logic for our loan application service are included.
After we have added these scenarios to our feature file, let's build the project and have a look at the Test Explorer. As you can see, Visual Studio has detected 10 tests in the project already. This can be easily explained when we look at the number of scenarios and examples in the feature file. Each scenario outline is interpreted as a group of tests, and each example is interpreted as a test. Individual scenarios, so ones that are not a Scenario Outline with examples, will also be interpreted as a test, but we don't have those in this example.
The Reqnroll extension here in Visual Studio also immediately gives us useful information here. All the steps in the scenarios are written in purple. A step in purple means that Reqnroll cannot determine which code, or in Reqnroll terms, which step definition, it should execute when the test runner encounters the step. This can have one of two reasons:
ONE no step definition can be found that matches the step TWO there's more than one step definition that matches the step, so we have an ambiguous step definition
Right now, we are clearly in the first situation, because we haven't created any step definitions yet. In both cases, Reqnroll will throw an exception and stop the execution of the test. Let's see what that looks like by running one of the tests.
As you can see, our test stops executing at the very first step, and the feedback we get is 'No matching step definition found for one or more steps.'. Let's work on improving that.
But first, what are step definitions? Step definitions are methods that are run when the Reqnroll runner of your choice encounters a certain step when executing a scenario. For each step in your feature files, there should be exactly one matching step definition method.
You can write these methods yourself, of course, but the Reqnroll extension for Visual Studio can also generate them for you, which is especially useful when you have a lot of new steps to implement, as we have here.
Right-click on the feature file and select 'Define Steps'. This opens a dialog where you can select the steps for which step definition methods will be generated, as well as the name of the class they should be placed in. The suggested name is fine with me right here.
If we click 'Create', a new class with the name we specified is added to our project. Also, the font colour for the steps in our feature file changes from purple to white, indicating that Reqnroll will now know which step definition method to execute when it encounters a step.
Let's run our scenario again and see if the output has changed.
And it has! Reqnroll still throws an exception, but this time, it's a different one, one stating that 'One or more step definitions are not implemented yet.'
Let's have a look at the step definitions that have been generated. The first thing you should be aware of is the [Binding] attribute that is added to the class containing step definitions. This attribute notifies Reqnroll that the class contains step definitions. When running scenarios, Reqnroll will only look in classes with this attribute for step definitions.
To demonstrate this, let's comment out the attribute and recompile our project. We see that Reqnroll no longer can find the step definitions, as the steps turn purple again. Let's revert that again. And now the step definitions can be located once more.
So, how does Reqnroll know WHICH of the step definition methods to run when it encounters a specific step in a scenario? The step definition method to be executed is determined by both looking at the Given, When or Then attribute, which should match the Gherkin keyword prefixing the step, and more importantly, the value of the argument passed to this attribute should match the step description.
So, this step definition method matches the step 'Given John is an active ParaBank customer', which means that this method is executed every time the Reqnroll runner encounters that exact step.
If we change the word 'customer' to 'client', the step definition doesn't match the step any longer. And now they match once again.
The reason that, even though we have generated step definitions for all the steps in our feature fure, Reqnroll still throws an exception, is because by default, when you generate step definitions in this way, it puts this line in the body of the method.
This serves as a reminder that you probably still need to do work to implement the behaviour described in the step, to really turn your features and scenarios into fully functioning automated acceptance tests. We will look at examples using various different libraries in Chapter 8. For now, we'll simply remove these statements and end up with step definition methods with an empty method body.
If we run our scenario once more, we see that execution is successful. All steps in the scenario have been implemented by step definitions, and all step definition methods were executed successfully, without any exceptions being thrown.
In the next chapter, we'll take a look at some of the ways we can make our step definitions much more versatile using parameterization and the power of regular expressions.
The current state of this project is available on GitHub, in the 'chapter5' branch of the repository you see linked in the video right now.