.NET Performance Blog

March 28, 2011

OpenFaq (Part 2): Business Requirements in Code

Filed under: .NET,ASP.NET MVC,BDD,General,TDD — Eric P @ 3:38 am

Previous posts in OpenFaq series
OpenFaq (Part1): The beginning

There has been a lot of discussion about self documenting code. You can use good class/method names, proper unit tests and comments to make it clear what the code does.
But how do you specify and enforce what the code is SUPPOSED to do from business perspective, versus what it does? Usually there is a separate Business requirements document with a set of User Acceptance criteria that is used to QA the application. What if you could automate user acceptance criteria tests and run them from beginning (even before writing any code). That’s where BDD comes in.

The Joy of BDD

Brandon Santrom has a nice presentation on using SpecFlow, WatiN with MVC to write acceptance tests:

Video is here:
http://channel9.msdn.com/Series/mvcConf/mvcConf-2-Brandom-Satrom-BDD-in-ASPNET-MVC-using-SpecFlow-WatiN-and-WatiN-Test-Helpers

MSDN article (that covers a bit different scenario) is here:
http://msdn.microsoft.com/en-us/magazine/gg490346.aspx

In the presentations, Brandon Santrom promotes the following way of doing devlelopment:
BDD Approach

The first module I wanted to implement for OpenFaq was UserModule which will handle CRUD for User and related objects and will also implement Custom Membership Provider that will use EF4.
But before doing that, following BDD, I will write some UAC tests to make sure that user can login and register using default Membership Provider that can be used with MVC 3.

Setting up solution

Before writing any tests I am going to setup a new solution/project for OpenFaq.
Following YAGNI, I will only create projects as I need them.

To Start I am going to have 3 projects:

  • OpenFaq.Web – MVC 3 project
  • OpenFaq.Web.Tests – unit tests for controllers
  • OpenFaq.AcceptanceTests – acceptance tests

I also setup NUGet package manager that ScottGu mentioned many times in his blog.
I used it to add references to WatiN, SpecFlow for “OpenFaq.AcceptanceTests” project. I noticed that NUGet was installing packages into “packages” directory under solution directory. Since my project structure is:

\src – solution goes here
\lib – external dependencies go here

So I changed NuGet package directory to put packages into “\lib” using instructions here:
http://stackoverflow.com/questions/4092759/is-it-possible-to-change-the-location-of-packages-for-nuget

Login & Register – Starting BDD

Before writing any new code I create the following two Features using SpecFlow:

Login Feature

Feature: Login a site user
	In order to use OpenFaq features
	As a site user
	I want to be able to login to the OpenFaq site

Scenario: Login with valid information
	Given I am on the site home page
	When I click the "Log On" link
	And I complete the form with the following information:
		| Field           | Value							|
		| UserName        | openfaquser1					|
		| Password        | password1						|
	And I click the "Log On" button
	Then I should see a link with the text "Log Off" on the page


Scenario: Login with invalid information
	Given I am on the site home page
	When I click the "Log On" link
	And I complete the form with the following information:
		| Field           | Value							|
		| UserName        | unknowuser						|
		| Password        | password1						|
	And I click the "Log On" button
	Then I should see a validation summary "Login was unsuccessful"
	And  I should see a field error "The user name or password provided is incorrect"

Register Feature

Feature: Register a new site user
	In order to ask/answer questions
	As a site user
	I want to be be able to register new account

@mytag
Scenario: Register with valid information
	Given I am on the site home page
		And I click the "Log On" link
		And I click the "Register" link	
	When I enter a random username
		And I complete the form with the following information:
			| Field				| Value					|
			| Email				| openfaquser@test.com	|
			| Password			| p@bla12				|
			| ConfirmPassword	| p@bla12				|
		And I click the "Register" button
	Then I should see a link with the text "Log Off" on the page

SpecFlow uses language called Gerhkin to describe business requirements. Not quite English and not quite code it is meant to bridge a gap between software developers and business analysts.
When you create SpecFlow feature files above, SpecFlow automatically creates CS files that interpret Gerhkin into C# code. When you run the tests, each step Given, When, Then… expects there to be a function that actually implements this functionality.

So to implement “Scenario: Login with valid information”, here is the file you would provide with Step Definitions:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenFaq.AcceptanceTests.StepHelpers;
using TechTalk.SpecFlow;
using WatiN.Core;

namespace OpenFaq.AcceptanceTests.Steps
{
	[Binding]
	public class Login
	{

		[Given(@"I am on the site home page")]
		public void GivenIAmOnTheSiteHomePage()
		{
			WebBrowser.Current.GoTo("http://localhost/OpenFaq.Web");
		}

		[When("I click the \"(.*)\" link")]
		public void WhenIClickALinkNamed(string linkName)
		{
			var link = WebBrowser.Current.Link(Find.ByText(linkName));

			if (!link.Exists)
				Assert.Fail(string.Format("Could not find '{0}' link on the page", linkName));

			link.Click();
		}

		[When(@"I complete the form with the following information:")]
		public void WhenICompleteTheFormWithTheFollowingInformation(Table table)
		{
			foreach (var tableRow in table.Rows)
			{
				var field = WebBrowser.Current.TextField(Find.ByName(tableRow["Field"]));

				if (!field.Exists)
					Assert.Fail(string.Format("Could not find {0} field on the page", field));

				field.TypeText(tableRow["Value"]);
			}
		}

		[When("I click the \"(.*)\" button")]
		public void WhenIClickAButtonWithValue(string buttonValue)
		{
			var button = WebBrowser.Current.Button(Find.ByValue(buttonValue));

			if (!button.Exists)
				Assert.Fail(string.Format("Could not find '{0}' button on the page", buttonValue));

			button.Click();
		}

		[Then("I should see a link with the text \"(.*)\" on the page")]
		public void ThenIShouldSeeALinkWithTheTextOnThePage(string linkText)
		{
			Assert.IsTrue(WebBrowser.Current.Link(Find.ByText(linkText)).Exists,
				string.Format("The following link text was not found on the page: {0}", linkText));
		}
	}
}

You may have noticed that in many cases instead of specifying actual text, I use a regular expression to pass Button value or link text to the function.

[Then("I should see a link with the text \"(.*)\" on the page")]

//instead of

[Then("I should see a link with the text \"Log On\" on the page")]

This will allow me to re-use the steps, in many different scenarios.

MembershipProvider – where art thou

When I ran acceptance tests I received errors having to do with MembershipProvider not being setup. To set it up on my local Sql Server DB I used steps here:
http://helios.ca/2009/04/22/aspnet-mvc-sqlmembershipprovider/

Now all acceptance tests have passed.

Tests Passed

Yes, I do use Resharper 5.1.

Did they live happily ever after?

Not quite yet…

In next part of this series I will replace SqlMembershipProvider with the one that supports EF 4.0 Code First.

For now you can get all the code from here:
http://openfaq.codeplex.com/releases/view/63346

Current project road map is here:
http://openfaq.codeplex.com/wikipage?title=Road%20Map%20%26%20Progress

Advertisements

1 Comment »

  1. […] Previous posts in OpenFaq series OpenFaq (Part 1): The beginning OpenFaq (Part 2): Business Requirements in Code […]

    Pingback by OpenFaq (Part 3): Custom Membership Provider in Entity Framework Code First « .NET Unplugged — April 5, 2011 @ 4:34 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: