I’ve seen some pretty bad SpecFlow code. Code that seems to violate every good practice out there. Poor reuse. Copy and paste everywhere. Test code is the hotel room of the software world. People are sloppier and more careless than they otherwise would be. I’m not sure why that is. Perhaps because tests are not seen as “real code”. But as a testing code base grows from dozens to hundreds to even a thousand or more test cases, having well-factored composable SpecFlow steps becomes critical.
For this post, I’d like to share a strategy I use to solve a common use case. It doesn’t take much effort and yields huge dividends over time.
Testing an Email Service
Most of my integration tests exercise web services, and many service operations require a request payload with setup spanning multiple Gherkin steps. Consider the example below. It validates a service responsible for sending marketing emails much like MailChimp’s Mandrill API.
The Gherkin and bindings represent a basic test that sends a “Hello World” email message. The first three Given
statements build out the email contents with a sender, recipients, and message body. Each one is responsible for updating the private field _sendRequest
with different bits of information. It makes sense to break these out from the subsequent When
and Then
statements that send the message and verify the response. This honors the Single Responsibility Principle and makes for better reuse.
SendRequestBuilder
The SendRequestBuilder
class is the first step toward a better solution. It contains the three Given
step bindings copied from SendMailSteps
. They act on an internal SendRequest
object exposed by the SendRequestBuilder.Instance
property. SpecFlow can bind to the step definitions as before.
SendMailSteps
cleans up nicely after introducing SendRequestBuilder
. The constructor takes an instance via dependency injection, and InvokeEndpoint()
grabs the request object before calling the service.
By moving these steps to a common builder, they can be shared among different feature classes.
Step Argument Transformations
While this is a move in the right direction, SendRequestBuilder
could be improved further. Often SpecFlow steps need to be called directly from code in addition to binding to the Gherkin. You may notice the same request being created in several places and decide to condense those steps into one. For example, the code snippet below builds the “Hello World” request in one step rather than three.
Notice how awkward it is to create and populate a SpecFlow table in code. Tables work well for bindings, but manipulating them directly is messy. A good solution is to use Step Argument Transformations to eliminate the table argument. Then the steps become much easier to call from code and can still be bound to tables.
Now the method GivenTheHelloWorldEmailMessage()
uses concrete types instead of tables. Much better.
Next Steps
As you can see, a little bit of encapsulation goes a long way. You could refactor this example further by moving the two Then
steps into a shared validation class. In fact, a mature SpecFlow code base will often contain many builders and validation objects injected into lightweight steps classes.