I have been working on some acceptance tests and found a valuable tool — SpecFlow. Nowadays, testing with SpecFlow/Cucumber or similar tools seems to gain speed, with more and more people consider utilising power of ATDD and BDD. Essentially, SpecFlow makes it possible for business people to define valuable behaviour for the project.
I have adapted code defined in Oren’s blog to show the simplicity of SF. In the post Oren presented new syntax for unit testing namely Arrange-Act-Assert. The types under test are defined as
public interface IUserRepository
{
User GetUserById(int id);
}
public interface INotificationSender
{
void Send(string message);
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class LoginController
{
private readonly IUserRepository repository;
private readonly INotificationSender sender;
public LoginController(IUserRepository repository, INotificationSender sender)
{
this.repository = repository;
this.sender = sender;
}
public void ForgotMyPassword(int userId)
{
User user = repository.GetUserById(userId);
sender.Send("Changed password for " + user.Name);
}
}
And the test looks like this:
[Test]
public void WhenUserForgetPasswordWillSendNotification_UsingExpect()
{
var userRepository = MockRepository
.GenerateStub<IUserRepository>();
var notificationSender = MockRepository
.GenerateMock<INotificationSender>();
userRepository.Stub(x => x.GetUserById(5))
.Return(new User { Id = 5, Name = "ayende" });
notificationSender.Expect(x => x.Send(null))
.Constraints(Text.StartsWith("Changed"));
new LoginController(userRepository, notificationSender)
.ForgotMyPassword(5);
notificationSender.VerifyAllExpectations();
}
Well, if I didn’t happen to know the programming language, I would immediately fail to understand what is happening. Let’s see how I can rewrite the test in a more domain-oriented and business-friendly way.
Firstly, using NuGet I install the following tools
PM> install-package SpecFlow
PM> install-package RhinoMocks
Secondly, I define a test from a business perspective. Tests are written in a Gherkin language.
Feature: Send Notification
In order to automate support services
And solve trivial problems quickly
As a company with high standard of support
We want to send emails
@sendNotification
Scenario: Send forgot password notification
Given user forgot password
When he clicks on a forgot password link
Then new password is sent
Now, as a developer I generate a “code behind” and insert it into the send notification feature steps
[Binding]
public class SendNotificationSteps
{
private IUserRepository userRepository;
private INotificationSender notificationSender;
[BeforeScenario]
public void Initialize()
{
userRepository = MockRepository.GenerateStub<IUserRepository>();
notificationSender = MockRepository.GenerateMock<INotificationSender>();
}
[Given(@"user forgot password")]
public void GivenUserForgotPassword()
{
userRepository.Stub(x => x.GetUserById(5))
.Return(new User { Id = 5, Name = "ayende" });
notificationSender.Expect(x => x.Send(null))
.Constraints(Text.StartsWith("Changed"));
}
[When(@"he clicked on a forgot password link")]
public void WhenHeClickedOnAForgotPasswordLink()
{
new LoginController(userRepository, notificationSender).ForgotMyPassword(5);
}
[Then(@"password changed notification is sent")]
public void ThenPasswordChangedNotificationIsSent()
{
notificationSender.VerifyAllExpectations();
}
}
So without altering Oren’s test (just rearranging), this is what I can get as the output

Conclussions:
SpecFlow allows non-technical folks to define features and correspondent tests. Each line of scenario corresponds to a method, where a technical dude writes code. Features are defined in a meta language Gherkin that can be learned within five minutes. SpecFlow supports NUnit, MSTest, MbUnit and any mocking framework. What I noticed so far it is a little bit tedious to configure tests on a build machine and as it is a fresh tool occasional error do occur (however they are not brain teasers and can be quickly fixed).
If you enjoyed this post, make sure you subscribe to my RSS feed!
f3536c49-5625-48b2-a926-6034d6bdb350|0|.0