[Home] Edit Article
Author Information
File Uploads
Edit Article XML Content
<?xml version="1.0"?> <document> <header> <issuecode> </issuecode> <articlecode> </articlecode> <zone> </zone> <title>Behavior-Driven Development Using SpecFlow</title> <authors> <author> <name>Mohammad Azam</name> <email>azamsharp@gmail.com</email> <bio> <p>Mohammad Azam is the founder of the knowledge-based website, www.highoncoding.com, which hosts more than 600 articles and several hundred videos. He is also a Microsoft ASP.NET MVP. At present Mohammad Azam is digging deep into the iPhone framework including the Cocos2d framework for game development. Azam also hosts a Cocos2d podcast which is available on iTunes.</p> <p> </p> <p>Azam is a frequent speaker at local Houston events including Houston TechFest. Azam has authored several articles for DevProConnections and CODE Magazine. When not programming, Azam likes to spend time with his lovely wife and beautiful daughter.</p> </bio> <photo>UNKNOWNIMAGE</photo> </author> </authors> <copyright>2012 - CoDe Magazine and EPS Software Corp.</copyright> <owner>CoDe Magazine</owner> </header> <body> <p id="1">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. </p> <h2>SpecFlow</h2> <p id="2">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. </p> <p id="3">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. </p> <h2>Scenario and Setup</h2> <p id="4">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.</p> <p id="5">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 <b>Package Manager Console </b>and issue the “<b>Install-Package SpecFlow</b>” command as shown in <b>Figure 1</b>.</p> <p id="6">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. </p> <h2>Writing User Registration Feature</h2> <p id="7">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 <i>Given</i>, <i>When</i>, <i>And</i>, <i>Then</i>, 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.</p> <p id="8">Add a new SpecFlow feature file to the project and name it <b>RegisterUser.feature</b>. The contents of RegisterUser.feature are shown below: </p> <codesnippet>»»»»»Feature:»Register»a»new»User</codesnippet> <codesnippet>In»order»to»use»the»features»provided»by»CodeBlog</codesnippet> <codesnippet>a»user»should»be»able»to»register»to»the»website</codesnippet> <codesnippet>Scenario:»Create»a»new»user»registration</codesnippet> <codesnippet>»»»»»»When»the»user»visits»the»registration»page</codesnippet> <codesnippet>»»»»»»And»enter»the»following»information</codesnippet> <codesnippet>»»»»»»|»Field»»»»|»Value»»»|</codesnippet> <codesnippet>»»»»»»|»username»|»johndoe»|</codesnippet> <codesnippet>»»»»»»|»password»|»star»»»»|</codesnippet> <codesnippet>»»»»»»And»click»the»"Register"»button</codesnippet> <codesnippet>»»»»»»Then»the»user»should»be»redirected»to»the</codesnippet> <codesnippet>confirmation»page</codesnippet> <p id="9">The feature file starts with the <i>Feature </i>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.</p> <p id="10">The <i>Scenario </i>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 <i>When</i>, <i>And </i>and <i>Then</i>. The <b>Field Value </b>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.</p> <p id="11">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. <b>Figure 2 </b>shows a failed test message.</p> <p id="12">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 <b>Listing 1</b>. </p> <p id="13"> <b>NOTE: </b>You can add a step definition file by using the Add New Item option. The Step Definition template is automatically installed with SpecFlow. </p> <p id="14">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.</p> <p id="15">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: </p> <codesnippet>»»»»»<font color="Blue">private»</font>IE»_ie»=»<font color="Blue">new»</font>IE();</codesnippet> <codesnippet>»»»»»[When(@"the»user»visits»the»registration»</codesnippet> <codesnippet>page")]</codesnippet> <codesnippet>»»»»»<font color="Blue">public»void»</font></codesnippet> <codesnippet>WhenTheUserVisitsTheRegistrationPage()</codesnippet> <codesnippet>»»»»»{</codesnippet> <codesnippet>»»»»»»_ie.GoTo("http://localhost:1064/Register/");</codesnippet> <codesnippet>»»»»»}</codesnippet> <p id="16">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: </p> <codesnippet>»»[TestClass]</codesnippet> <codesnippet> <font color="Blue">public»class»</font>when_requesting_index_view</codesnippet> <codesnippet>{</codesnippet> <codesnippet>»»»»[TestMethod]</codesnippet> <codesnippet>»»»»<font color="Blue">public»void»</font>should_return_successfully()</codesnippet> <codesnippet>»»»»{</codesnippet> <codesnippet>»»»»»»»»<font color="Blue">var»</font>controller»=»<font color="Blue">new»</font>RegisterController();</codesnippet> <codesnippet>»»»»»»»»<font color="Blue">var»</font>result»=»(ViewResult)»</codesnippet> <codesnippet>controller.Index();</codesnippet> <codesnippet>»»»»»»»»Assert.AreEqual("Index",result.ViewName);</codesnippet> <codesnippet>»»»»}</codesnippet> <codesnippet>}</codesnippet> <p id="17">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 <b>Listing 2</b>.</p> <p id="18"> <b>NOTE: </b>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. </p> <p id="19">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: </p> <codesnippet>@using(Html.BeginForm(<font color="Blue">new»</font>{»Action»=»"New"»}))»</codesnippet> <codesnippet>{</codesnippet> <codesnippet> </codesnippet> <codesnippet><text>User»Name:»</text>»»</codesnippet> <codesnippet>@Html.TextBox("username")»</codesnippet> <codesnippet><br»/></codesnippet> <codesnippet><text>Password:</text>»@Html.TextBox("password")</codesnippet> <codesnippet> </codesnippet> <codesnippet><input»type="submit"»id="registerButton"»</codesnippet> <codesnippet>name="registerButton"»value="Register"»/></codesnippet> <codesnippet>»»»»</codesnippet> <codesnippet>}</codesnippet> <p id="20">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.</p> <p id="21">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.</p> <p id="22">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 <b>Listing 3 </b>shows the controller test. </p> <p id="23">The test in <b>Listing 3 </b>directed the implementation of the controller’s “New” action, which is shown below: </p> <codesnippet>»»»<font color="Blue">public»</font>ActionResult»<font color="Blue">New</font>(<font color="Blue">string»</font>username,»<font color="Blue">string</font></codesnippet> <codesnippet>password)</codesnippet> <codesnippet>»»»»»»»»{</codesnippet> <codesnippet>»»»»»»»»»»»»<font color="Blue">var»</font>isSaved»=»_repository.Save(<font color="Blue">new»</font></codesnippet> <codesnippet>User()»{UserName»=»username,»Password»=»</codesnippet> <codesnippet>password});</codesnippet> <codesnippet>»»»»»»»»»»»»<font color="Blue">if»</font>(isSaved)»<font color="Blue">return»</font>View("Confirm");»</codesnippet> <codesnippet>»»»»»»»»»»»»<font color="Blue">return»</font>View("Index");»</codesnippet> <codesnippet>»»»»»»»»}</codesnippet> <p id="24">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: </p> <codesnippet> <font color="Blue">public»</font>RegisterController()»:»this(<font color="Blue">new»</font></codesnippet> <codesnippet>UserRepository())</codesnippet> <codesnippet>»»»»»»»»{</codesnippet> <codesnippet>»»»»»»»»»»»</codesnippet> <codesnippet>»»»»»»»»}</codesnippet> <codesnippet> </codesnippet> <codesnippet>»»»»»»»»<font color="Blue">public»</font>RegisterController(IUserRepository»</codesnippet> <codesnippet>repository)</codesnippet> <codesnippet>»»»»»»»»{</codesnippet> <codesnippet>»»»»»»»»»»»»_repository»=»repository;»</codesnippet> <codesnippet>»»»»»»»»}</codesnippet> <p id="25">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.</p> <p id="26">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. </p> <h2>Conclusion</h2> <p id="27">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. </p> </body> <sidebars> <sidebar title="Framework References"> <p id="28">SpecFlow Framework (specflow.org)</p> <p id="29">Cucumber (cukes.info)</p> <p id="30">StructureMap(structuremap.net/structuremap/)</p> <p id="31">Ninject (ninject.org)</p> <p id="32">Unity (unity.codeplex.com)</p> </sidebar> </sidebars> <figures> <figure id="1" src="UNKNOWNIMAGE"> <b>Figure 1: </b>NuGet Package Manager Console.</figure> <figure id="2" src="UNKNOWNIMAGE"> <b>Figure 2: </b>SpecFlow message showing the steps required to implement the story.</figure> </figures> <tables> </tables> <codelistings> <codelisting id="0" header="Listing 1: Paste the step definitions from the error message into the step definitions file."> <code>[When(@"the»user»visits»the»registration»page")]</code> <code>»»»»»»»»<font color="Blue">public»void»</font>WhenTheUserVisitsTheRegistrationPage()</code> <code>»»»»»»»»{</code> <code>»»»»»»»»}</code> <code> </code> <code>[When(@"enter»the»following»information")]</code> <code>»»»»»»»»<font color="Blue">public»void»</font>WhenEnterTheFollowingInformation(Table»table)</code> <code>»»»»»»»»{</code> <code>»»»»»»»»}</code> <code> </code> <code>[When(@"click»the»""Register""»button")]</code> <code>»»»»»»»»<font color="Blue">public»void»</font>WhenClickTheRegisterButton()</code> <code>»»»»»»»»{</code> <code>»»»»»»»»}</code> <code> </code> <code>»»»»»»»»[Then(@"the»user»should»be»redirected»to»the»</code> <code>confirmation»page")]</code> <code>»»»»»»»»<font color="Blue">public»void</font></code> <code>ThenTheUserShouldBeRedirectedToTheConfirmationPage()</code> <code>»»»»»»»»{</code> <code>»»»»»»»»}</code> </codelisting> <codelisting id="21" header="Listing 2: The step where the user enters the registration information on the page"> <code>»[When(@"enter»the»following»information")]</code> <code>»»»»»»»»<font color="Blue">public»void»</font>WhenEnterTheFollowingInformation(Table»table)</code> <code>»»»»»»»»{</code> <code>»»»»»»»»»»»»<font color="Blue">foreach»</font>(<font color="Blue">var»</font>tableRow»<font color="Blue">in»</font>table.Rows)</code> <code>»»»»»»»»»»»»{</code> <code>»»»»»»»»»»»»»»»»<font color="Blue">var»</font>field»=»</code> <code>_ie.TextField(Find.ByName(tableRow["Field"]));</code> <code> </code> <code>»»»»»»»»»»»»»»»»<font color="Blue">if»</font>(!field.Exists)</code> <code>»»»»»»»»»»»»»»»»{</code> <code>»»»»»»»»»»»»»»»»»»»»Assert.Fail("Field»does»not»exist!");</code> <code>»»»»»»»»»»»»»»»»}</code> <code> </code> <code>»»»»»»»»»»»»»»»»field.TypeText(tableRow["Value"]);</code> <code>»»»»»»»»»»»»}</code> <code>»»»»»»»»}</code> <code>»»[When(@"click»the»""Register""»button")]</code> <code>»»»»»»»»<font color="Blue">public»void»</font>WhenClickTheRegisterButton()</code> <code>»»»»»»»»{</code> <code>»»»»»»»»»»»»_ie.Button("registerButton").Click();</code> <code>»»»»»»»»}</code> </codelisting> <codelisting id="42" header="Listing 3: The mock controller test of the repository layer"> <code>[TestClass]</code> <code>»»»»<font color="Blue">public»class»</font>when_registration_is_successfull</code> <code>»»»»{</code> <code>»»»»»»»»[TestMethod]</code> <code>»»»»»»»»<font color="Blue">public»void»</font>should_return_the_confirmation_view()</code> <code>»»»»»»»»{</code> <code>»»»»»»»»»»»»<font color="Blue">var»</font>repositoryStub»=»</code> <code>MockRepository.GenerateStub<IUserRepository>();</code> <code>»»»»»»»»»»»»repositoryStub.Stub(x»=>»x.Save(<font color="Blue">new»</font></code> <code>User())).Return(<font color="Blue">true</font>).IgnoreArguments();»</code> <code>»»»»»»»»»»»»<font color="Blue">var»</font>controller»=»<font color="Blue">new»</font></code> <code>RegisterController(repositoryStub);</code> <code>»»»»»»»»»»»»<font color="Blue">var»</font>result»=»(ViewResult)»</code> <code>controller.New("johndoe",»"star");»</code> <code>»»»»»»»»»»»»Assert.AreEqual("Confirm",result.ViewName);</code> <code>»»»»»»»»}</code> <code>»»»»}</code> </codelisting> </codelistings> </document>