If you’re reading this article, I suspect you already know what SpecFlow and BDD are, so I won’t insist on this. If you are not yet familiar with SpecFlow, check out this article series written by Bas Dijkstra. In short, SpecFlow is a C# framework that supports BDD. What this means is that the tests are written in Gherkin syntax, and each Gherkin step has a corresponding method in C#, called step definition.
In this article, I’ll show you some useful regular expressions in SpecFlow that have the potential to improve your tests.
What are regular expressions?
Regular expressions (also known as regex) are sequences of characters that define a pattern. In SpecFlow, we have feature files where we define the test scenarios using the Gherkin syntax (Given – When – Then), and each step is matched by a step definition, which is the connection between the feature file and the application interface. This connection is done through regular expressions.
Basic Gherkin test steps definition
Here’s how a typical scenario looks, in the Gherkin language:
Scenario: Add items to cart Given I am logged in as a customer When I search for laptop And I add the first item to the cart Then the cart has 1 item
We can use SpecFlow’s Visual Studio extension to generate the test steps definitions, where we will add the code that will execute the steps:
For example, the first step method will match this regular expression:
[Given(@"I am logged in as a customer")]
This means that the step text in Gherkin will have to match 100% with the regular expression between the quotes. If we have something different, such as:
Given I am logged in as a seller
The step will not be recognized, and a different step definition method will need to be created for it. This is pretty simple if our steps will always be repeated using the exact same phrasing. However, experience teaches us that this will unlikely be the case 😅
Logical OR in step definitions
Let’s say we want to reuse the step definition for sellers as well. However, we don’t want the admin login to be performed in the same method, because it requires other parameters (most probably your application won’t work like this, but for the sake of our demonstration, let’s say it does). This means that we want the step definition to match exactly “Given I am logged in as a customer” or “Given I am logged in as a seller”. The only difference is the last word. We can use the logical OR in our regular expression, to make the steps match the step definition:
[Given(@"I am logged in as a customer|seller")]
And we can have a separate step definition for the admin:
[Given(@"I am logged in as an admin")]
Regular expressions for string parameters
Let’s use the second step to demonstrate this. For this specific test, I want to search for the word “laptop”, but I might want to repeat the search for different items. The method should be the same but receive a different string parameter each time. The SpecFlow annotation for this step will be:
[When(@"I search for (.*)")]
You can see now that the word “laptop” is replaced by the regular expression (.*), which means that this definition will match all the steps that contain any string in its place, even no string. If needed, this string can also be used as a parameter in the method.
If we want our steps to have the string written between single or double quotes, we need to reflect this in the step annotation as well. This translates into ‘(.*)’ for simple quotes, and “”(.*)””” for the double-quotes. In the latter case, we need extra quotes to escape special characters in the regular expression.
Regular expressions for integers
Let’s take a look now at the last step. We can have an exact match for it if we use the regular expression [Then(@”the cart has 1 item”)]. But there’s no point in having different step definitions for different numbers because the method will always perform the same check, just against different values. So if we want our expression to match any integer number, but not decimals or strings, the step definition will look like this:
[Then(@"the cart has (\d*) item")]
This will match the step above, or the same step, but with a different number of items.
Defining steps with optional characters
But using the example above, when we expect to have more than 1 item, we won’t express it the same, will we? 🧐 Because in normal, plain English, we use the plural form of the noun item. This means our step will be:
Then the cart has 5 items
And that extra ‘s’ character will cause the step not to match the previous definition. As mentioned before, we don’t want to create different methods that do the same thing. So we need to make sure that our previous step definition will match this step, too. We can do this by making the ‘s’ character optional. That’s easily accomplished by adding a question mark after the optional character:
[Then(@"the cart has (\d*) items?")]
We can also use this if we have a step that might contain a decimal number, but it also could be just an integer. To exemplify this, let’s consider we have another step that verifies the cots. If the cost is a decimal, then we express it as such. However, if the total adds up to a round number, we will probably write it without any 0 digits after the decimal point. So we might have any one of these:
And the total cost is 99.99 dollars. And the total cost is 100 dollars
The regular expression to match both steps is:
[Then(@"the total cost is (\d+\.?\d*) dollars")]
SpecFlow is quite flexible in the way it allows binding between the Gherkin steps and the step definitions. Using regular expressions in SpecFlow helps us avoid code duplication, which in turn makes test maintenance easier.