SpecFlow Step Definition with Optional Parameter

by Oliver 13. May 2013 11:41

Today, this question came up on the SpecFlow Google Group:

Assuming I would like to define in Gherkin the following:

1. When I send some argument xxx with parameter aaa and another parameter bbb

2. When I send some argument xxx with parameter aaa

And I would like to have only one reusable function, something like this:

[When(@"I send some argument (.*) with parameter (.*) and another parameter (.*)")]

public void foo(string arg, string paramA, string paramB)

{

  // check if paramB is null and do something

}

I am aware of the table feature (pipe separated values) but I would like to stick with this text-alike syntax.

We've encountered this use case several times in the past (also avoiding the table syntax) and used to solve it by delegating the shorter version to the longer one but I decided to go see if I can find a more elegant solution.

Matching the steps

The first step at matching both steps was to simply match the first step. Since version 1.9 SpecFlow has this wonderful syntax highlighting in .feature files which helps identify unbound steps:

image

We can see, that our first pattern is too greedy and matches the second step but not the way we need.

Changing the regular expression for the first parameter to something more restrictive, allows us to restrict the match to only the first step (notice that the second step has been colored purple to notify us of the fact that there is no matching step definition, yet):

image

The regex ([^ ]*) we use here means that we match all characters that are not spaces 0 to n times, thus denying the match of the second step because of the space character following the argument aaa. Sometimes, though, you also need to match spaces in arguments and that's when we use a slightly modified version like this:  "([^"]*)"  which means: match a quote, then match everything but a quote 0 to n times and save this match as a reference, and then match another quote. In a verbatim string (prefixed by the @ sign) this will look like this:

image

Note, that now you'll have to enclose your spaced string value in quotes, though, but you can still use the same method to put that step attribute on.

Now, let's go for the second argument.

Using a .NET Optional Parameter

My first try was to add an optional parameter to the method we already have and provide it with a default argument like this:

image

Unfortunately, SpecFlow complains that for the first step with only one argument the matching method needs to have only one parameter. I thought that the compiler would generate two methods here, one with and one without the optional parameter so that at runtime it could pick the right one depending on which parameters were provided with a  value. It turns out that this is not so. Seems that IL code for a method with optional parameters contains only one method, as well, as per this article:

Intermediate language for optional parameter method: IL

.method private hidebysig static void Method([opt] int32 'value', [opt] string name) cil managed
{
    .param [1] = int32(1)
    .param [2] = string('Perl')
    .maxstack 8
    L_0000: ldstr "value = {0}, name = {1}"
    L_0005: ldarg.0
    L_0006: box int32
    L_000b: ldarg.1
    L_000c: call void [mscorlib]System.Console::WriteLine(string, object, object)
    L_0011: ret
}
That's why SpecFlow complains.

Solution: Use Two Methods

It looks like there is not direct solution to the problem that would require only a single method. The solution we employ seems to be all you can do about it, at least right now with SpecFlow 1.9. Which would be to use (at least) two separate methods, one of which delegates its execution to the other, more general one:

image

Happy spec'ing!

Comments are closed

About Oliver

shades-of-orange.com code blog logo I build web applications using ASP.NET and have a passion for javascript. Enjoy MVC 4 and Orchard CMS, and I do TDD whenever I can. I like clean code. Love to spend time with my wife and our children. My profile on Stack Exchange, a network of free, community-driven Q&A sites

About Anton

shades-of-orange.com code blog logo I'm a software developer at teamaton. I code in C# and work with MVC, Orchard, SpecFlow, Coypu and NHibernate. I enjoy beach volleyball, board games and Coke.