Implementing an architecture with ASP.NET MVC (Part 3) – The business layer

Introduction

The focus of this post it to describe how I go about developing the business layer. This post follows on from my previous post  ASP.NET MVC – Creating an application with a defined architecture. In my previous post, I was fulfilling a requirement to fetch a list of customers and display them on a page with ASP.NET MVC. So I will continue on with that as an example.

The Plan

  • At the end of the previous post, i had an object called “CustomerAgent” that just created two instances of the “customer” object. This is going to be replaced with a call the business layer to fetch a list of customers. The business layer will be returning the customers as a message type. The CustomerAgent will map the message type to the customer object that is already defined in the “PresentationProcesses” assembly. We will drive this out with a test.
  • In the business layer, we will need to respond to the call for fetching a list of customers. Our business layer will ask a “repository” to fetch customers from a datastore. The business layer will take a list of customers and map them into a message that will be returned to the caller.
  • In the next post to continue on from this one, the repository will need to get the customers from somewhere and map them into instances of objects that represent a customer in the domain model. An ORM tool will simplify this process.

While implementing this plan, we will be testing the interactions between layers. We will also be registering the more types in our IOC container.

Putting the plan into action

The CustomerAgent is not under test and currently returns fake instances, so we will create a new test assembly and place the “customer agent” under test and start driving out the interaction with the business layer.

  1. Create a new class library in your solution called “Web.PresentationProcesses.Specs”.
  2. Add a project reference to the “Web.PresentationProcesses” project.
  3. Add file references to “Nunit.framework”, “Rhino.Mocks” and “NSpec” (or whatever unit testing framework and mocking tool use want to use).
  4. Add a new class (test fixture) to this new assembly called “Fetching_a_list_of_customers” and decorate it with the “[TestFixture]” attribute.
  5. Add a test called “Should_fetch_and_return_customers”. This test will return a list of “customers” and assert that the result was not null. At this point the test will pass as the “customer agent” is still just returning two made up instances. Here is the test (its not the final test, its going to change)
    [TestFixture]
    public class Fetching_a_list_of_customers
    {
        private ICustomerAgent agent;
    
        [SetUp]
        public void SetUp()
        {
            agent = new CustomerAgent();
        }
    
        [Test]
        public void Should_fetch_and_return_customers()
        {
            List<Customer> customers = agent.GetCustomerList();
    
            customers.ShouldNotBeNull();
        }
    }
  6. Currently the “Customer Agent” is a public class, i don’t want my implementations to be public. The interface will be only way that the above layer can communicate with this. But we will still want our tests to be able to work with the concrete implementation and also so will our mocking tool. In the “Web.PresentationProcesses” assembly, open up “AssemblyInfo.cs”. Add the following lines and save and close the file.
    [assembly: InternalsVisibleTo("Web.PresentationProcesses.Specs")]
    
    [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
  7. Next step, change the “CustomerAgent” class to be “internal” instead of “public”. Run our tests to verify that it all still works.

Driving out the business layer

As mentioned earlier the service is going to return data as a message type. This is going to use a very common message pattern being “Request/Response” which is also known as the “request/reply” pattern.

The business layer is going to be in its own assembly, it will run in-process to the MVC application. We could in the future without to much effort, place the business layer behind a WCF endpoint and host the business layer in another process. I personally would not to this by default, the reasons for making the business layer remote must be because you either have more than one application that interacts with the business layer tier or because of scalability. Scalability over performance its down to your applications needs, availability and size of the user base. Moving the business layer to run out of process is another blog post that i will write as the final post of this series as optional approach. Although the its not that different.

At the moment we have no business layer, so from the test i above, we start defining the interface (contract) in the business layer.

I am a big fan of Resharper, it makes my world a better place. It saddens me to think that developers are out their coding without the fruits that resharper gives.

  1. Back into the unit test, cutting a not such a long story to be a shorter story. I am going to set up an expectation on an interface and return a response object. I have also driven out the properties that are in the response object. The test is also asserting that the “customer” UI object is populated with the values from the response  Here is the test fixture. I have created the new types within the same code file as the fixture. I do this sometimes while i am cutting new code and creating new types, then i will (“with the help of resharper”) move the classes into there own files and move them into the correct assemblies. Which i do as the next step.
    using System.Collections.Generic;
    using NBehave.Spec.NUnit;
    using NUnit.Framework;
    using Rhino.Mocks;
    using Web.PresentationProcesses.Customers;
    
    namespace Web.PresentationProcesses.Specs
    {
        [TestFixture]
        public class Fetching_a_list_of_customers
        {
            private ICustomerAgent agent;
            private ICustomerService service;
    
            [SetUp]
            public void SetUp()
            {
                service = MockRepository.GenerateMock<ICustomerService>();
    
                agent = new CustomerAgent();
            }
    
            [Test]
            public void Should_fetch_and_return_customers()
            {
                FetchCustomerResponse response = GetResponse();
    
                service.Expect(x => x.FetchCustomers()).Return(response);
    
                List<Customer> customers = agent.GetCustomerList();
    
                customers.ShouldNotBeNull();
                customers.Count.ShouldEqual(response.Customers.Count);
                IsMappingCorrect(response.Customers[0], customers[0]).ShouldBeTrue();
    
                service.AssertWasCalled(x => x.FetchCustomers());
            }
    
            private FetchCustomerResponse GetResponse()
            {
                return new FetchCustomerResponse
                {
                    Customers = new List<CustomerInfo>
                    {
                        new CustomerInfo
                        {
                            AccountManagerName = "Mr Account Manager",
                            AccountNumber = "ABC 123",
                            City = "Some Town",
                            Country = "Some Country",
                            Name = "Happy Customer"
                        }
                    }
                };
            }
    
            private bool IsMappingCorrect(CustomerInfo customerInfo, Customer customer)
            {
                return customerInfo.AccountManagerName == customer.AccountManagerName &&
                       customerInfo.AccountNumber == customer.AccountNumber &&
                       customerInfo.City == customer.City &&
                       customerInfo.Country == customer.Country &&
                       customerInfo.Name == customer.Name;
            }
        }
    
        public class FetchCustomerResponse
        {
            public List<CustomerInfo> Customers { get; set; }
        }
    
        public class CustomerInfo
        {
            public string Name { get; set; }
            public string AccountNumber { get; set; }
            public string AccountManagerName { get; set; }
            public string City { get; set; }
            public string Country { get; set; }
        }
    
        public interface ICustomerService
        {
            FetchCustomerResponse FetchCustomers();
        }
    }
  2. At the moment, the code will compile, but the test will fail. As mentioned in the last step, I am going to move the “FetchCustomerResponse”, “CustomerInfo” and “ICustomerService”  into another assembly.
    1. Add a new class library to the solution called “Web.Business”
    2. Create a folder called “Contracts” and move the “ICustomerService” into this folder and change the namespace to match it new location.
    3. In the Contracts folder, add a new folder called “Messages”. Move the CustomerInfo and FetchCustomerResponse types into this new folder and change the namespaces.
  3. In both the “Web.PresentationProcesses” and “Web.PresentationProcesses.Specs”, add a project reference to “Web.Business”.
  4. The CustomerAgent needs to be able to talk to the “ICustomerService”, change the constructor of the “CustomerAgent” to be passed a reference of “ICustomerService” and hold the reference in a variable in the “CustomerAgent”. The “SetUp” on the unit test will change to pass in the service into the constructor of the “CustomerAgent”.
  5. Now to make the test pass, the code in the method ”GetCustomerList” in the “CustomerAgent” has been replaced to make the test pass. Here is that code for the modified “CustomerAgent” as well as the changes to the SetUp method in the test.
    // Unit test
    
    [SetUp]
    public void SetUp()
    {
        service = MockRepository.GenerateMock();
    
        agent = new CustomerAgent(service);
    }
    
    // Customer agent
    
    using System.Collections.Generic;
    using System.Linq;
    using Web.Business.Contracts;
    using Web.Business.Contracts.Messages;
    
    namespace Web.PresentationProcesses.Customers
    {
        internal class CustomerAgent : ICustomerAgent
        {
            private readonly ICustomerService service;
    
            public CustomerAgent(ICustomerService service)
            {
                this.service = service;
            }
    
            public List<Customer> GetCustomerList()
            {
                List<Customer> result = new List<Customer>();
    
                FetchCustomerResponse response = service.FetchCustomers();
    
                result.AddRange((from custInfo in response.Customers
                    select new Customer
                    {
                        AccountManagerName = custInfo.AccountManagerName,
                        AccountNumber = custInfo.AccountNumber,
                        City = custInfo.City,
                        Country = custInfo.Country,
                        Name = custInfo.Name
                    }).ToList());
    
                return result;
            }
        }
    }
  6. All the tests, now pass. Now to create a concrete “CustomerService”. Create a new folder in the “Web.Business” assembly called “Customers” and add a new internal class called “CustomerService”.
  7. Make the “CustomerService” implement the “ICustomerService” interface. We will come back to the method “FetchCustomers” later, so just throw a NotImplemented exception for minute.
  8. We need to register the types in the IOC container. We need to pass the IOC container to the “Web.Business” assembly so that it can register its types.
    1. Add a reference to Unity or what ever IOC container that you are using.
    2. Create a public class called “BusinessModule” and add a public method below.
      public class BusinessModule
      {
          public void Configure(IUnityContainer container)
          {
              container.RegisterType<ICustomerService, CustomerService>();
          }
      }
    3. In the “Web.PresentationProcesses” assembly, add a reference to the “Web.Business” assembly.
    4. In the “PresentationProcessesModule”, in the configure method, create a new instance of the “BusinessModule” and call the “Configure” method passing in the container.

The Customer Service

Now to implement the CustomerService. The service itself is just a facade and brings its internals together to provide a simple API. The service will delegate to objects that have the responsibility to carry out required actions. In the case of the CustomerService, it will ask a repository to return a list of customers. The customers will be instances of a domain entity called “Customer”. The service will map the domain type to the message type.

I keep the domain isolated from the outside world. The only way to interact with the domain from the outside world is through a service. The service does not contain much logic, if it did it would mean that logic is not in the domain and the domain would be not be rich. A thin domain and rich services would the “anemic domain model” anti-pattern. In this example, I am pulling data out of a repository, so their is no business logic.

  1. Firstly, create a new class library assembly called “Web.Business.Specs” which you may have guessed is going to hold the tests for the business assembly. Add references to NUnit and Moq/RhinoMocks or what ever is your preferred mocking tool.
  2. We are going to be testing internal objects within the web.business project. As described earlier on in this post. Add this two lines to the assemblyInfo.cs in the “web.business” project.
    [assembly: InternalsVisibleTo("Web.Business.Specs")]
    [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
  3. Create a new test fixture called “Fetching_customers”. Our is going to ask the service to provide a list of customers. This information will be provided a list of “CustomerInfo” objects contained in the “FetchCustomerResponse”. Here is the test.
    using System.Collections.Generic;
    using NBehave.Spec.NUnit;
    using NUnit.Framework;
    using Rhino.Mocks;
    using Web.Business.Contracts;
    using Web.Business.Contracts.Messages;
    using Web.Business.Domain;
    using Web.Business.Persistence;
    using Web.Business.Services;
    
    namespace Web.Business.Specs
    {
        [TestFixture]
        public class Fetching_customers
        {
            private ICustomerService customerService;
            private ICustomerRepository customerRepository;
    
            [SetUp]
            public void SetUp()
            {
                customerRepository = MockRepository.GenerateMock<ICustomerRepository>();
    
                customerService = new CustomerService(customerRepository);
            }
    
            [Test]
            public void Should_return_a_list_containing_customer_information()
            {
                List<Customer> customers = new List<Customer>
                {
                    new Customer
                    {
                        AccountManagerName = "Mr A Manager",
                        AccountNumber = "ABC 123",
                        City = "Some Place",
                        Country = "Some Island",
                        Name = "Some Customer"
                    }
                };
    
                customerRepository.Expect(x => x.FindAll()).Return(customers);
    
                FetchCustomerResponse response = customerService.FetchCustomers();
    
                customerRepository.AssertWasCalled(x => x.FindAll());
    
                response.Customers.Count.ShouldEqual(customers.Count);
    
                IsMappingCorrect(response.Customers[0], customers[0]).ShouldBeTrue();
            }
    
            private bool IsMappingCorrect(CustomerInfo customerInfo, Customer customer)
            {
                return customerInfo.AccountManagerName == customer.AccountManagerName &&
                       customerInfo.AccountNumber == customer.AccountNumber &&
                       customerInfo.City == customer.City &&
                       customerInfo.Country == customer.Country &&
                       customerInfo.Name == customer.Name;
            }
        }
    }
  4. The above test drove out the customer Repository interface and a domain object called customer. At the moment i have added two new folders “Persistence” and “Domain” to the “Web.Business” project and place the customer object in the domain folder and repository interface in the “Persistence” folder. Getting a step ahead, i have created a concrete implementation of CustomerRepository. Here is the code for the new types:
    namespace Web.Business.Domain
    {
        internal class Customer
        {
            public string Name { get; set; }
            public string AccountNumber { get; set; }
            public string AccountManagerName { get; set; }
            public string City { get; set; }
            public string Country { get; set; }
        }
    }
    
    //
    
    using System.Collections.Generic;
    using Web.Business.Domain;
    
    namespace Web.Business.Persistence
    {
        internal interface ICustomerRepository
        {
            List<Customer> FindAll();
        }
    }
    
    //
    
    using System.Collections.Generic;
    using Web.Business.Domain;
    
    namespace Web.Business.Persistence
    {
        internal class CustomerRepository : ICustomerRepository
        {
            public List<Customer> FindAll()
            {
                return new List<Customer>();
            }
        }
    }
  5. Lastly,  wire up the repository in IOC container, the configure method in “BusinessModule” class should look like this.
    public void Configure(IUnityContainer container)
    {
        container.RegisterType<ICustomerService , CustomerService>();
        container.RegisterType<ICustomerRepository , CustomerRepository>();
    }

That’s it for this post. the next step is to use an ORM to fetch the data from the database, which will be the responsibility of the repository.

ASP.NET MVC: A utility for testing secured Actions

I have come across a common scenario while developing an ASP.NET MVC application. This being testing Actions that require the user to be authorised and / or requires permissions to execute methods  that are locked down with principal permissions.

Here as an example of a controller with an action that is firstly decorated with the “Authorize” attribute (ASP.NET MVC filter) that requires the user to be in a specified role.

[Authorize(Roles="Client Administrators")]
public ViewResult Edit(int clientId)
{
    return View("Edit");
}

 

While I was writing a test for an Action that needed the user to be Authorized . I   created implementations of the IIdentity and IPrincipal interfaces that I could use to satisfy this requirement. Although this worked, it was not reusable. When you set principle, you should apply it to the current AppDomain instead of the current thread as the every thread created in your AppDomain will have the principal set automatically. Once you have set the principle you cannot apply my than once as it will throw an exception. It is also a problem is you try to reset the principal to null.

To add to matters, is was not long before I needed to satisfy principal permissions. My Principal and Identity implementations needed to be more flexible.

So the answer was to create a utility that firstly set the AppDomain to use my principal implementation and allow each and every test to arrange the state of the principal. For each and every test  I also want to write the minimum amount of code by only writing one line of code.  This line of code would be amongst NBehave and RhinoMocks code that have their own fluent interfaces so I want my line of code to fit in with the fluent style. Below are three ways I want to my utility.

//1
TheCurrentUser.Instance.IsAuthenticated().And.IsInAllRoles();
 
//2
TheCurrentUser.Instance.IsAuthenticated()
    .And.IsInThisRole("System admins")
    .And.IsInThisRole("developers")
    .And.IsInThisRole("power users");
 
//3
TheCurrentUser.Instance.IsNotAuthenticated();

 

So I started to create this utility by placing its into its own assembly. The root object is the “TheCurrentUser” object. Its the only public class in the assembly and is used as  a singleton. It creates and hosts three objects references and sets the IPrincipal implementation to the current AppDomain.

using System;
using System.Security.Principal;
 
namespace RussellEast.Principal.Utility
{
    public class TheCurrentUser
    {
        static readonly TheCurrentUser instance = new TheCurrentUser();
 
        private IPrincipal principal;
        private IAuthenticationOptions options;
        private ISecurityContext context;
 
        private TheCurrentUser()
        {
            BuildObjectGraph();
            SetAppDomainPrincipal();
        }
 
        static TheCurrentUser() { }
 
        public static IAuthenticationOptions Instance
        {
            get { return instance.Options; }
        }
 
        public IAuthenticationOptions Options
        {
            get { return options; }
        }
 
        private void BuildObjectGraph()
        {
            context = new SecurityContext();
            principal = new FakePrincipal(context);
 
            options = AuthenticationOptions.Create(context);
        }
 
        private void SetAppDomainPrincipal()
        {
            AppDomain.CurrentDomain.SetThreadPrincipal(principal);
        }
    }
}

 

The fluent interface uses a context object (that holds the state as its passed around) that is injected into  various objects that are explained below. The context object implements the following interface.

using System.Collections.Generic;
 
namespace RussellEast.Principal.Utility
{
    internal interface ISecurityContext
    {
        bool IsAuthenticated { get; set; }
        bool IsInAllRoles { get; set; }
        List<string> Roles { get; set; }
        void Reset();
    }
}

 

This is the concrete implementation of the ISecurityContext interface.

using System.Collections.Generic;
 
namespace RussellEast.Principal.Utility
{
    class SecurityContext : ISecurityContext
    {
        public bool IsAuthenticated { get; set; }
        public bool IsInAllRoles { get; set; }
        public List<string> Roles { get; set; }
 
        public void Reset()
        {
            IsAuthenticated = false;
            IsInAllRoles = false;
 
            if (Roles != null)
            {
                Roles.Clear();
            }
        }
    }
}

 

Back to the “TheCurrentUser” object, the instance property returns an object that implements the following interface below.

(Although this object is a singleton, the pattern would be to return the singular  instance of the singleton, i have not done this as the singleton does nothing but host objects and be the root object for the fluent interface.)

namespace RussellEast.Principal.Utility
{
    public interface IAuthenticationOptions
    {
        IAuthorisationRouter IsAuthenticated();
        void IsNotAuthenticated();
    }
}

 

My concrete of the above  interface is as follows:

namespace RussellEast.Principal.Utility
{
    class AuthenticationOptions : IAuthenticationOptions
    {
        private readonly IAuthorisationRouter router;
        private readonly ISecurityContext context;
        private AuthenticationOptions(ISecurityContext context, IAuthorisationRouter router)
        {
            this.context = context;
            this.router = router;
        }
 
        public IAuthorisationRouter IsAuthenticated()
        {
            ChangeSecurityState(true);
 
            return router;
        }
 
        public void IsNotAuthenticated()
        {
            ChangeSecurityState(false);
        }
 
        private void ChangeSecurityState(bool isAuthenticated)
        {
            context.Reset();
 
            context.IsAuthenticated = isAuthenticated;
        }
 
        public static IAuthenticationOptions Create(ISecurityContext context)
        {
            var roleAuthorisation = new RoleAuthorisation(context);
            var router = new AuthorisationRouter(roleAuthorisation);
 
            return new AuthenticationOptions(context, router);
        }
    }
}

 

As you can see from the code, the AuthenticationOptions class is passed an instance of the security context and an instance of the IAuthorisationRouter.

The responsibility of this class is to reset the context and set a new authentication value. This class also contains a “Factory Method” that is used to create an instance of this object.

The “IsAuthenticated” method returns the next link in the method chain being the “IAuthorisationRouter”.

namespace RussellEast.Principal.Utility
{
    public interface IAuthorisationRouter
    {
        IRoleAuthorisation And { get; }
    }
}

 

The “IAuthorisationRouter” purpose in life is to provide a linkage in the method chain. Here is the concrete implementation.

namespace RussellEast.Principal.Utility
{
    class AuthorisationRouter : IAuthorisationRouter
    {
        private IRoleAuthorisationController roleAuthorisation;
 
        public AuthorisationRouter(IRoleAuthorisationController roleAuthorisation)
        {
            this.roleAuthorisation = roleAuthorisation;
            this.roleAuthorisation.Router = this;
        }
 
        public IRoleAuthorisation And
        {
            get { return roleAuthorisation; }
        }
    }
}

 

I am using constructor injection as much as possible. It was not so clear cut with this object as the AuthorisationRouter exposes an instance of “RoleAuthorisation” which implies that it has knowledge of it. The “RoleAuthorisation” exposes an instance of the AuthorisationRouter. So both need to know about each other.  I am using interfaces to define the overall fluent interface so I want to keep it clean from any implementation that is needed to wire references to together. So the solution is to create another interface that is only used by a specific consumer  (Interface Segregation Principal). Because I need two instances of different objects that need to know about each other, I have to construct one object first that then inject that into the other object when its be instantiated. Then I use a setter (setter injection) to provide the reference of the second object back to the first object.

namespace RussellEast.Principal.Utility
{
    internal interface IRoleAuthorisationController :
        IRoleAuthorisation, ISettableRouter
    {
    }
}

 

Here is the “ISettableRouter” interface:

namespace RussellEast.Principal.Utility
{
    internal interface ISettableRouter
    {
        IAuthorisationRouter Router { set; }
    }
}

 

Here is the “IRoleAuthorisation” interface.

namespace RussellEast.Principal.Utility
{
    public interface IRoleAuthorisation
    {
        void IsInAllRoles();
        IAuthorisationRouter IsInThisRole(string role);
    }
}

 

I have a concrete class “RoleAuthorisation” that implements the “IRoleAuthorisationController” interface. Its responsibility is to manage the list of roles.

using System.Collections.Generic;
 
namespace RussellEast.Principal.Utility
{
    class RoleAuthorisation : IRoleAuthorisationController
    {
        private IAuthorisationRouter router;
        private readonly ISecurityContext context;
 
        public RoleAuthorisation(ISecurityContext context)
        {
            this.context = context;
        }
 
        public IAuthorisationRouter Router
        {
            set { router = value;  }
        }
 
        public void IsInAllRoles()
        {
            context.IsInAllRoles = true;
        }
 
        public IAuthorisationRouter IsInThisRole(string role)
        {
            if (!string.IsNullOrEmpty(role))
            {
                if (context.Roles == null)
                {
                    context.Roles = new List<string>();
                }
 
                if (!context.Roles.Contains(role))
                {
                    context.Roles.Add(role);
                }
            }
 
            return router;
        }
    }
}

 

That’s all the interfaces and classes needed to build the fluent interface. The last objects needed is the Principal and Identity objects, which reads its values from the context.

using System.Security.Principal;
 
namespace RussellEast.Principal.Utility
{
    class FakePrincipal : GenericPrincipal
    {
        private readonly ISecurityContext context;
 
        public FakePrincipal(ISecurityContext context) : base(new FakeIdentity(context), null)
        {
            this.context = context;
        }
 
        public override bool IsInRole(string role)
        {
            if (context.IsAuthenticated)
            {
                return context.IsInAllRoles ? true : context.Roles.Contains(role);
            }
 
            return false;
        }
 
        class FakeIdentity : GenericIdentity
        {
            private readonly ISecurityContext context;
 
            public FakeIdentity(ISecurityContext context) : base("Fake user")
            {
                this.context = context;
            }
 
            public override bool IsAuthenticated
            {
                get { return context.IsAuthenticated; }
            }
         }
    }
}

 

Although a lot of code is written for the fluent interface which took two hours to build from scratch, I have a very flexible utility that I know I am going to use a lot and not just with ASP.NET MVC. The only refactoring that I want to perform is to change the name of some of the interfaces as I feel that they could be better described. To finish this post, here is an example using this utility in a unit test.

 

 
        [Specification]
        public void Should_be_able_to_edit_a_client()
        {
            ViewResult result = null;
 
            story.WithScenario("editing a client")
                .Given("an instance of the controller", () => controller.ShouldNotBeNull())
                .And("the current user belongs to the 'client administrators' role", () =>
                {
                    TheCurrentUser.Instance.IsAuthenticated()
                        .And.IsInThisRole("Client Administrators");
 
                    //NOTE: these two lines are not needed, just  validating the utility.
                    Thread.CurrentPrincipal.Identity.IsAuthenticated.ShouldBeTrue();
                    Thread.CurrentPrincipal.IsInRole("Client Administrators").ShouldBeTrue();
                })
                .When("the Edit action is invoked", () =>
                {
                    result = controller.Edit(1) as ViewResult;
                })
                .Then("verify that the user is directed to the right view", () =>
                {
                    result.AssertViewRendered().ForView("Edit");
                });
 
        }

 

For a better understanding of the unit test above, please read this post

TDD/BDD with ASP.NET MVC

I have recently been going hammer and tongs with ASP.NET MVC. One of the selling points for ASP.NET MVC is its testability, so while I have been learning this technology, I have found a couple of annoyances.

When you create a new ASP.NET MVC project, the wizard in VS2008 asks you if you want to create a test project. If you do create the test project, you get a couple of pre made tests that really don’t portray good TDD and because you only get a couple of tests, you have low code coverage and have untested code. I have had to back peddle  tests for the existing code.

For doing any development with ASP.NET MVC, the MvcContrib project is an absolute must. The MvcContrib project contains general features that really improve your development, Dependency Injection, type safety, a Grid and much more. I found that the MvcContrib examples, are so straight forward that in 60 seconds of eyeballing the code I could understand it fully.

Keeping to the subject, MvcContrib provides a library that really help when it comes to testing. I have been developing with MonoRail fairly solidly for the last 6 months or so and their are similarities between the Castle test classes and the Library from MvcContrib.

To make things more real world:

  • My project is using Unity for its IoC container and I am injecting dependancies into my controllers (again the MvcContrib provides libraries that make this easy to implement). 
  • For writing my tests/specs, I am using NBehave, NUnit and RhinoMocks 3.5. 

I started rewriting the tests for the HomeController because is was the easiest. The tests check that the controller displays the right view.

using System.Web.Mvc;
using RussellEast.Mvc.Controllers;
using MvcContrib.TestHelper;
using NBehave.Narrator.Framework;
using NBehave.Spec.NUnit;
using NUnit.Framework;
using Specification = NUnit.Framework.TestAttribute;

namespace RussellEast.Mvc.Tests.Controllers.Home
{
    [TestFixture]
    public class When_displaying_home_pages : SpecBase
    {
        private HomeController controller;
        private Story story;
        private TestControllerBuilder builder;

        [Story]
        public override void MainSetup()
        {
            base.MainSetup();

            story = new Story("Home page");

            story.AsA("user")
                .IWant("to go to the home page")
                .SoThat("i can start using the application");

            PrepareController();
        }

        private void PrepareController()
        {
            builder = new TestControllerBuilder();

            controller = builder.CreateController();
        }

        [Specification]
        public void Index()
        {
            ActionResult result = null;

            story.WithScenario("home index page")
                .Given("the controller has been prepared", () => controller.ShouldNotBeNull())
                .When("the user navigates to the index view", () => result = controller.Index())
                .Then("verify that the result is correct", () => result.AssertViewRendered().ForView("Index"));
        }

        [Specification]
        public void About()
        {
            ActionResult result = null;

            story.WithScenario("about page")
                .Given("the controller has been prepared", () => controller.ShouldNotBeNull())
                .When("the user navigates to the about view", () => result = controller.About())
                .Then("verify that the result is correct", () => result.AssertViewRendered().ForView("About"));
        }
    }
}

The controller code is:

 

    public class HomeController : Controller
    {
        [Authorize, DefaultAction]
        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Sample text being displayed";

            return View("Index");
        }

        public ActionResult About()
        {
            ViewData["Title"] = "About Page";

            return View("About");
        }
    }

Note: to order for the above tests to pass, you need to supply the name of the view to render.

The tests above cover the behaviours for the HomeController which are very basic, so to raise the bar. The ASP.NET MVC project template comes with an AccountController that works with a membership provider and Forms Authentication. The controller has a constructor that accepts two parameters. Because the “System under test” is the Account Controller i am going to stub the dependencies and write tests for the “Login” and  “Logout” actions.

 

using System.Web.Mvc;
using System.Web.Security;
using RussellEast.Mvc.Controllers;
using MvcContrib.TestHelper;
using NBehave.Narrator.Framework;
using NBehave.Spec.NUnit;
using NUnit.Framework;
using Rhino.Mocks;
using Specification = NUnit.Framework.TestAttribute;

namespace EWS.Steeple.Tests.Controllers.Authentication
{
    [TestFixture]
    public class When_authenticating : SpecBase
    {
        private AccountController controller;
        private IFormsAuthentication formsAuthentication;
        private MembershipProvider membershipProvider;

        private Story story;

	[Story]
        public override void MainSetup()
        {
            base.MainSetup();

            story = new Story("Authenticating");

            story.AsA("known user to the system")
                .IWant("to be authenticated")
                .SoThat("i can use the system");

            PrepareController();
        }

        private void PrepareController()
        {
            TestControllerBuilder builder = new TestControllerBuilder();

            formsAuthentication = MockRepository.GenerateStub();
            membershipProvider = MockRepository.GenerateStub();

            controller = builder.CreateController(new object[] {formsAuthentication, membershipProvider});
        }

        [Specification]
        public void Should_log_in_with_user_name_and_password_while_not_caching_details()
        {
            string username = string.Empty;
            string password = string.Empty;
            bool rememberMe = false;

            ActionResult result = null;

            story.WithScenario("log in with validate details")
                .Given("a controller instance", ()=> controller.ShouldNotBeNull())
                .And("a user name", () => username = "Username")
                .And("a password", () => password = "password")
                .When("i attempt to log in", () =>
                {
                    membershipProvider.Expect(x => x.ValidateUser(username, password)).Return(true);
                    formsAuthentication.Expect(x => x.SetAuthCookie(username, rememberMe));

                    result = controller.Login(username, password, rememberMe, string.Empty);
                })
                .Then("redirect user to the home page", () =>
                {
                    result.AssertActionRedirect().ToAction(x => x.Index());

                    formsAuthentication.VerifyAllExpectations();
                    membershipProvider.VerifyAllExpectations();
                });
        }

        [Specification]
        public void Should_sign_out_and_return_to_login_when_logging_out()
        {
            ActionResult result = null;

            story.WithScenario("log out and return to login view")
                .Given("a controller instance", () => controller.ShouldNotBeNull())
                .When("i log out", () =>
                {
                    formsAuthentication.Expect(x => x.SignOut());

                    result = controller.Logout();
                })
                .Then("i expect to be sent to the login view", () =>
                {
                    result.AssertActionRedirect().ToAction("Login");

                    formsAuthentication.AssertWasCalled(x => x.SignOut());
                });
        }
    }
}

Some points to be aware of.

  • I have used two different ways to test the ActionResult was a redirect. The first and my preferred way is using the generic controller type and the lambda expression. I have been forced to use the string argument approach due to the fact that the “Login” action is overloaded. It complained when trying to use a lambda approach.;-(.
  • MvcContrib test library provides a “TestControllerBuilder” that not only creates that instance of the controller, but mocks out the HttpContext, Sessions and form data. The docs for MvcContrib explains all. I have used this in my “PrepareController” method. 
  • The default code in the AccountController has not been changed to suit these tests.