Posts Tagged Folders
BDD with SpecFlow, NUnit and MVC3–View a List Of Products
In the last post I set up an MVC project and Specs project and added various assembly references to the Specs project. In this post I’ll write my first feature which is to view a list of products.
The First Feature
Add a features folder to the Specs project then add a new item.
If you have installed SpecFlow correctly you should be able to add a SpecFlow feature. I will call mine ViewAListOfProducts.feature. I am not sure if this is the right level of granularity for a feature but ManageProducts sounds a bit too wide. I can always refactor later if this proves wrong. I am also unsure how to best organize my features in folders but will start with one folder for features and reorganise later if necessary.
Next I need to detail the feature, this is usually in the format:
Feature: feature
In order gain a benefit
As a user
I want feature
In my case it seems reasonable to do:
Feature: View a list of products
In order to see what products there are
As a user
I want to view a list of products
The First Scenario
Then comes the first scenario, ‘Display the default products view’, and where I run into naming trouble. I decided that I would start testing at the controller rather than the interface so, as much as I would like to write out the scenario as:
Scenario: Display the default products view
When I navigate to /product/
Then the products page should be displayed
And the page should contain a list of products
That sounds misleading as I am not navigating to a URL or displaying a page. I’ll use a more honest description:
Scenario: Display the default products view
When the product controller is told to display the default view
Then the controller should return a view
And the view title should be products
And the view should contain a list of products
This better describes what I want to do but is less easily understood by any non technical stakeholder (which is one of the benefits of BDD). I think it is probably better than the alternatives in my case which are either testing the GUI or a misleading scenario.
Compile and Run
Specflow creates a set of tests when the project is compiled which can then be run in the normal test runner. I use Resharper and the results are:
The tests are ignored but the output of the tests provides the code you need to write the steps file.
The Steps
Add a Steps directory to the Specs project and add a new item. again if Specs is installed correctly you should be able to add a SpecFlow Step Definition. I’ll call mine ViewAListOfProducts.cs.
Adding the code output from the test run gives results in this:
[Binding]
public class ViewAListOfProducts {
[When(@"the product controller is told to display the default view")]
public void WhenTheProductControllerIsToldToDisplayTheDefaultView() {
ScenarioContext.Current.Pending();
}
[Then(@"the controller should return a view")]
public void ThenTheControllerShouldReturnAView() {
ScenarioContext.Current.Pending();
}
[Then(@"the view title should be products")]
public void ThenTheViewTitleShouldBeProducts() {
ScenarioContext.Current.Pending();
}
[Then(@"the view should contain a list of products")]
public void ThenTheViewShouldContainAListOfProducts() {
ScenarioContext.Current.Pending();
}
}
I can then code WhenTheProductControllerIsToldToDisplayTheDefaultView to the simplest thing that works:
[When(@"the product controller is told to display the default view")]
public void WhenTheProductControllerIsToldToDisplayTheDefaultView() {
ProductController controller = new ProductController();
ActionResult result = controller.Index();
}
Of course now I need to create the controller which I will do with the MVC scaffolding. Although I don’t need it yet, I know I’ll eventually need the CRUD methods so will scaffold them all.
Running the tests after scaffolding shows the first method as done and the rest as pending. The second method, ThenTheControllerShouldReturnAView, is easy enough. Moving the result declaration out to the class allows:
[Then(@"the controller should return a view")]
public void ThenTheControllerShouldReturnAView() {
_result.ShouldBeType(typeof (ViewResult));
}
This is the first time I have used the Should framework and I really like the readability it gives the tests. Running the tests now shows the first two methods as done. I guess I could force a failure on the type test to get a red light first but that would seem like moving backwards to go forwards.
The remaining methods are as easy to complete and the final class looks like this.
[Binding]
public class ViewAListOfProducts {
[When(@"the product controller is told to display the default view")]
public void WhenTheProductControllerIsToldToDisplayTheDefaultView() {
var controller = new ProductController();
_result = controller.Index();
}
[Then(@"the controller should return a view")]
public void ThenTheControllerShouldReturnAView() {
_result.ShouldBeType(typeof (ViewResult));
}
[Then(@"the view title should be products")]
public void ThenTheViewTitleShouldBeProducts() {
ViewResult.ViewData["Title"].ShouldEqual("Products");
}
[Then(@"the view should contain a list of products")]
public void ThenTheViewShouldContainAListOfProducts() {
ViewResult.ViewData.Model.ShouldBeType(typeof (List<Product>));
}
private ActionResult _result;
private ViewResult ViewResult {
get { return (ViewResult) _result; }
}
}
Getting these tests to pass does not seem to drive out much code:
public ActionResult Index() {
ViewData["Title"] = "Products";
return View(new List<Product>());
}
Plus, of course an empty Product class. It is the simplest code to make the tests pass but at this stage it doesn’t seem the most efficient way to write code.
The next stage will be to expand our specs and code and hopefully it will drive the design down to the data layer and prove a bit more efficient.
Solution Layout for an MVC App
While it may seem easier to throw all code into one project, it is good practise to separate areas of code into different projects within a solution. This helps promote good design with principles like loose coupling and high cohesion. A solution layout I like is:
- Domain – contains the domain layer and data interfaces (in their own sub folder) and, if necessary can reference the Infrastructure project.
- DataInterfaces *
- Infrastructure – contains plumbing code for functions such as logging and reporting, should not reference any other projects.
- Logging
- Reporting
- Infrastructure.Data – contains data access code and references the Core and Infrastructure projects. Whilst some may put this in the infrastructure project, I prefer Domain to be able to reference Infrastructure which causes a circular reference if data access code is kept there.
- Tests – self explanatory – references any projects that need tests. Each project has a subfolder to contain it’s tests
- Core
- Data
- Web.Controllers
- Web – contains the core of the MVC project including the views and Global.asax, some files and folders have been moved around and models and controllers have been moved into different projects. The web project references all other projects except Test.
- Public
- css
- images
- javascript
- Views
- Public
- Web.Controllers – contains the controllers and also the RouteRegistrar class. Splitting the controllers into their own project does cause some extra initial work but a good argument for doing this can be found here.
*Indented items represent folders and sub folders within the projects.
The Solution Explorer should look similar to this when done:
All projects are class libraries except for the Web project which is MVC2. Web Content folder has been renamed to public and sub folders added and script and css files moved into it. HomeController has been moved to the Controllers project and the Controllers and Models folders have been deleted.
To get this solution to run:
- Set Web as Start Up Project
- Add a reference to Web.Controllers from Web.
- Add a references to System.Web, System.Web.MVC (2.0.0.0) and System.Web.Routing from Web.Controllers.
- Add the RouteRegistrar class to Web.Controllers.
- Amend Global.asax.cs Application_Start to use RouteRegistrar.RegisterRoutes.
using System.Web.Mvc;
using System.Web.Routing;
namespace Furld.Web.Controllers {
public class RouteRegistrar {
#region Class Methods
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {controller = "Home", action = "Index", id = UrlParameter.Optional} // Parameter defaults
);
}
#endregion
}
}
I also have a preferred folder structure which looks like this:
- Main project folder – this is what gets committed to source control
- Docs
- Lib – contains all libraries I reference (e.g. nunit.framework.dll, ninject.dll etc).
- Src – contains the solution file and the projects, each in it’s subfolder (Core, Data …).
- Tools – contains tools used in the project, both those that require installation and those where I am just using libraries (e.g. NUnit-2.5.4.10098.zip).
This layout has a few changes from my normal layout partly prompted by Rob Conery’s http://mvcstarter.codeplex.com/ and some investigation of http://www.sharparchitecture.net/.