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
Then statements that send the message and verify the response. This honors the Single Responsibility Principle and makes for better reuse.
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.
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.