As software development becomes complicated, writing unit tests provides some protection against constant changes and modifications. Traditionally, unit tests were written by testing each piece of the application layer in isolation. With the advent of behavior-driven development, now our unit tests can be composed into user defined stories. Each story represents a single feature of the application which can be tested from end to end. This method makes sure that the unit test only passes when the story is completely done. In this article, I’ll show you how to use SpecFlow and WatiN to write BDD-style tests to implement user stories.

SpecFlow

SpecFlow is inspired by Cucumber framework in the Ruby on Rails world. Cucumber uses plain English in the Gherkin format to express user stories. Once the user stories and their expectations are written, the Cucumber gem is used to execute those stores. SpecFlow brings the same concept to the .NET world and allows the developer to express the feature in plain English language.

You can download the SpecFlow framework from the official website, specflow.org. The installation files also install the Visual Studio template for SpecFlow, which you’ll use when writing user stories.

Scenario and Setup

In this article I will show you how to implement the user registration feature which is a common feature implemented on most of the websites.

The solution comprises of two different projects. One project is an ASP.NET MVC project and the other one is a Microsoft Unit Test project. Before I show you how to start writing the features, you need to add the SpecFlow framework to your test project. You can accomplish this in different ways but the easiest method is to use NuGet to download and reference the SpecFlow libraries. From within Visual Studio launch the Package Manager Console and issue the “Install-Package SpecFlow” command as shown in Figure 1.

Make sure that the Default project in Package Manager Console is set up as the MS Test project and not the web application project. Issuing the above command will add a SpecFlow reference to the test project. Repeat the same procedure for installing the WatiN framework which will be used to automate the user interface. After installing SpecFlow and WatiN, you are ready to write some BDD-style unit tests.

Writing User Registration Feature

Each feature is written in a SpecFlow feature file which can be added to the project. SpecFlow uses a special format when defining the features. The format is known as Gherkin format and it consists of special English keywords like Given, When, And, Then, etc. These keywords along with the English language allows the feature files to be easily read by developers as well as the non-technical staff and bridge the communication gap between them.

Add a new SpecFlow feature file to the project and name it RegisterUser.feature. The contents of RegisterUser.feature are shown below:

     Feature: Register a new User
In order to use the features provided by CodeBlog
a user should be able to register to the website
Scenario: Create a new user registration
      When the user visits the registration page
      And enter the following information
      | Field | Value |
      | username | johndoe |
      | password | star |
      And click the "Register" button
      Then the user should be redirected to the
confirmation page

The feature file starts with the Feature keyword, which defines the main purpose of the feature. The next few lines represent the description of the feature, which further illustrates the business need of the feature.

The Scenario keyword describes a particular scenario within the current feature. A single feature can consist of multiple scenarios. A scenario is described in plain English with some reserved keywords like When, And and Then. The Field Value pair, which is displayed in the form of a table, is used to inject input parameters into the stories. You can send in plain values instead of defining the table but in my experience, using a table is much easier to implement as well as to modify.

After defining the feature in the feature file, run the unit test. You will see that the test result is inconclusive since we have not defined the steps for our feature. Figure 2 shows a failed test message.

The error message indicates that in order to run the test we must have the step definitions defined. The easiest way is to copy the step definitions from the error message and paste it in the step definitions file as shown in Listing 1.

NOTE: You can add a step definition file by using the Add New Item option. The Step Definition template is automatically installed with SpecFlow.

In order to pass the feature you need to implement the above methods. During the implementation of the above methods you’ll be constantly jumping between the different layers of the application and implementing unit tests. The reason for this fast switching between different layers is that your primary purpose is to implement the feature, and during the implementation you will also be implementing the unit test for the dependencies, which will help to pass the feature.

In this article I’ll use the Microsoft Test framework to run the unit tests. The first task is to let the user browse to the registration page. Take a look at the following implementation:

     private IE _ie = new IE();
     [When(@"the user visits the registration
page")]
     public void
WhenTheUserVisitsTheRegistrationPage()
     {
      _ie.GoTo("http://localhost:1064/Register/");
     }

This example uses the WatiN framework to automate the user interface. When you run the above test it will fail because Register controller as well as the Index action do not exist. The next step is to implement the Register controller and the actions associated with the failing test. You’ll use the same test-driven approach to implement the Register controller. The following unit test makes sure that the Register view is returned:

  [TestClass]
public class when_requesting_index_view
{
    [TestMethod]
    public void should_return_successfully()
    {
        var controller = new RegisterController();
        var result = (ViewResult)
controller.Index();
        Assert.AreEqual("Index",result.ViewName);
    }
}

After the controller test passes you can return to the Registration feature and run it again. This time it will launch the default page of the Register controller in IE but it still fails. Next, you will implement the step where the user enters the registration information on the page. The step is defined in Listing 2.

NOTE: This example uses the table feature of the SpecFlow framework. This allowed you to quickly and cleanly inject the control name along with their values.

The above test will fail because there are no input controls on the page with the expected names. So, next you will implement the controls inside the Index.cshtml page as shown in the implementation below:

@using(Html.BeginForm(new { Action = "New" }))
{
    
<text>User Name: </text>
@Html.TextBox("username")
<br />
<text>Password:</text> @Html.TextBox("password")
    
<input type="submit" id="registerButton"
name="registerButton" value="Register" />
    
}

Run the test again and you will notice that the input controls are populated with the test data specified in the feature file and the register button gets invoked.

Unfortunately, the test fails since there is no “New” action specified in the RegisterController. Just like before, implement the controller test and make sure the correct view is returned from the RegisterController.

Now, you can test whether the controller returned the correct view once the user was registered successfully or not. The focus is the controller’s behavior and for that reason you’ll mock out the repository layer and return the expected result. The implementation in Listing 3 shows the controller test.

The test in Listing 3 directed the implementation of the controller’s “New” action, which is shown below:

   public ActionResult New(string username, string
password)
        {
            var isSaved = _repository.Save(new
User() {UserName = username, Password =
password});
            if (isSaved) return View("Confirm");
            return View("Index");
        }

Return back to the SpecFlow test and run the test again. You will notice that the test fails in the “New” action since the repository has not been initialized. You can easily solve this by calling the overloaded constructor from the default constructor as shown below:

public RegisterController() : this(new
UserRepository())
        {
           
        }
    
        public RegisterController(IUserRepository
repository)
        {
            _repository = repository;
        }

This is the simplest method to pass the test. If you constantly have multiple dependencies then you should be using a dependency injection framework to inject the dependencies such as StructureMap, Ninject, Unity, etc.

After making the changes, run the SpecFlow test again and you will notice that the feature now passes the test. This proves that the feature has been completed and is functional end to end. You can use the same steps above to implement the next feature in the list.

Conclusion

Unit testing provides the essence for creating professional and maintainable applications. Traditionally, the unit testing paradigm did not focuse on the user stories. Behavior-driven development demonstrates the importance of user stories in a domain-centric application.