Phil Fresle's Developer Blog

Unit Testing an ASP.NET MVC 4 Controller using MS Test, Rhino Mocks, AutoMapper and Dependency Injection

I decided to put together a demo project to showcase unit testing an ASP.NET MVC controller. The MVC controller is part of a much larger n-tier solution that stores data in SQL Server, uses Entity Framework, has a data layer using the Repository and Unit of Work patterns, and a service layer on top, but you will see from the testing that all this complexity is hidden and the front end MVC application could be layered on top of mush as far as the MVC and Test projects are concerned.

The controller is designed to implement the standard CRUD (Create, Read, Update, Delete) functionality exposed by a domain service.

The domain model is a simple one, defined as a POCO, and used to manipulate information about a company's branches, namely the "code" they are known by within the company, and their "name".

namespace DemoProject.Model
{
    public class Branch
    {
        public int Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
    }
}

The domain model would be mapped to a View Model for display in the MVC application. In this case there is a 1:1 mapping. This is not always the case as sometimes you do not wish to expose all the domain properties on a view.

using System.ComponentModel.DataAnnotations;

namespace DemoProject.Web.ViewModels
{
    public class BranchViewModel
    {
        public int Id { get; set; }

        [StringLength(10), Required]
        public string Code { get; set; }

        [StringLength(100), Required]
        public string Name { get; set; }
    }
}

The controller is fairly standard other than having the application's Branch Service and the AutoMapper Mapping Engine injected into it as part of the constructor. I used Ninject to perform the injection but any IoC engine would work as well.

These are the two relevant lines of code used in the RegisterServices method of NinjectWebCommon:

kernel.Bind<IMappingEngine>().ToConstant(Mapper.Engine);
kernel.Bind<IBranchService>().To<BranchService>();

These are the lines of code used to setup the AutoMapper mappings:

// domains models to view models
configuration.CreateMap<Branch, BranchViewModel>();
// view models to domain models
configuration.CreateMap<BranchViewModel, Branch>();

AutoMapper is not an essential tool but it removes some of the monotonous repetitive coding of assigning properties from the domain model to the view model and vice versa.

The BranchService that is called from the controller implements this interface:

using System.Collections.Generic;
using DemoProject.Model;

namespace DemoProject.Services
{
    public interface IBranchService
    {
        IEnumerable<Branch> GetAllBranches();
        Branch GetBranchById(int id);
        void CreateNewBranch(Branch branch);
        void ModifyBranch(Branch branch);
        void DeleteBranch(int id);
    }
}

Here is the controller code:

using System.Web.Mvc;
using AutoMapper;
using DemoProject.Model;
using DemoProject.Services;
using DemoProject.Web.ViewModels;

namespace DemoProject.Web.Controllers
{
    public class BranchController : Controller
    {
        private readonly IBranchService _branchService;
        private readonly IMappingEngine _mappingEngine;

        public BranchController(IBranchService branchService, IMappingEngine mappingEngine)
        {
            _branchService = branchService;
            _mappingEngine = mappingEngine;
        }

        public ActionResult Index()
        {
            var vm = new BranchIndexViewModel();
            vm.BranchList = _branchService.GetAllBranches();

            return View(vm);
        }

        public ActionResult Details(int id = 0)
        {
            var dm = _branchService.GetBranchById(id);

            if (dm == null)
                return HttpNotFound();

            // map domain properties to populate view model
            var vm = _mappingEngine.Map<Branch, BranchViewModel>(dm);

            return View(vm);
        }

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(BranchViewModel vm)
        {
            if (ModelState.IsValid)
            {
                // map view model properties to populate domain model
                var dm = _mappingEngine.Map<BranchViewModel, Branch>(vm);

                _branchService.CreateNewBranch(dm);

                return RedirectToAction("Index");
            }

            return View(vm);
        }

        public ActionResult Edit(int id = 0)
        {
            var dm = _branchService.GetBranchById(id);

            if (dm == null)
                return HttpNotFound();

            // map domain properties to populate view model
            var vm = _mappingEngine.Map<Branch, BranchViewModel>(dm);

            return View(vm);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(BranchViewModel vm)
        {
            if (ModelState.IsValid)
            {
                // map view model properties to populate domain model
                var dm = _mappingEngine.Map<BranchViewModel, Branch>(vm);

                _branchService.ModifyBranch(dm);

                return RedirectToAction("Index");
            }

            return View(vm);
        }

        public ActionResult Delete(int id = 0)
        {
            var dm = _branchService.GetBranchById(id);

            if (dm == null)
                return HttpNotFound();

            // map domain properties to populate view model
            var vm = _mappingEngine.Map<Branch, BranchViewModel>(dm);

            return View(vm);
        }

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            _branchService.DeleteBranch(id);

            return RedirectToAction("Index");
        }
    }
}

Finally, here are all the unit tests:

using System.Collections.Generic;
using System.Web.Mvc;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using DemoProject.Model;
using DemoProject.Services;
using DemoProject.Web.Controllers;
using DemoProject.Web.ViewModels;

namespace DemoProject.Tests.Web
{
    [TestClass]
    public class BranchControllerTests
    {
        private IBranchService _mockService;
        private IMappingEngine _mockMapper;
        private BranchController _controller;

        [TestInitialize]
        public void TestInitialize()
        {
            _mockService = MockRepository.GenerateMock<IBranchService>();
            _mockMapper = MockRepository.GenerateMock<IMappingEngine>();

            _controller = new BranchController(_mockService, _mockMapper);
        }

        [TestCleanup]
        public void TestCleanup()
        {
            _mockService = null;
            _mockMapper = null;

            _controller.Dispose();
            _controller = null;
        }

        #region Index Action Tests
        [TestMethod]
        public void Index_Action_Calls_BranchService_GetAllBranches()
        {
            //Arrange
            _mockService.Stub(x => x.GetAllBranches()).Return(null);

            //Act
            _controller.Index();

            //Assert
            _mockService.AssertWasCalled(x => x.GetAllBranches());
        }

        [TestMethod]
        public void Index_Action_Returns_ViewResult()
        {
            //Arrange
            _mockService.Stub(x => x.GetAllBranches()).Return(null);

            //Act
            var result = _controller.Index();

            //Assert
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

        [TestMethod]
        public void Index_Action_Returns_DefaultView()
        {
            //Arrange
            _mockService.Stub(x => x.GetAllBranches()).Return(null);

            //Act
            var result = _controller.Index() as ViewResult;

            //Assert
            Assert.AreEqual("", result.ViewName);
        }

        [TestMethod]
        public void Index_Action_Returns_View_With_BranchIndexViewModel()
        {
            //Arrange
            _mockService.Stub(x => x.GetAllBranches()).Return(null);

            //Act
            var result = _controller.Index() as ViewResult;

            //Assert
            Assert.IsInstanceOfType(result.Model, typeof(BranchIndexViewModel));
        }

        [TestMethod]
        public void Index_Action_Returns_View_With_ViewModel_Containing_Same_Data()
        {
            //Arrange
            var branches = new List<Branch>();
            branches.Add(new Branch { Id = 1, Code = "a", Name = "aaa" });
            branches.Add(new Branch { Id = 2, Code = "b", Name = "bbb" });

            _mockService.Stub(x => x.GetAllBranches()).Return(branches);

            //Act
            var viewResult = _controller.Index() as ViewResult;
            var viewModel = viewResult.Model as BranchIndexViewModel;

            //Assert
            Assert.AreSame(branches, viewModel.BranchList);
        }
        #endregion

        #region Details Action Tests
        [TestMethod]
        public void Details_Action_Calls_BranchService_GetBranchById()
        {
            //Arrange
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(null);

            //Act
            _controller.Details(1);

            //Assert
            _mockService.AssertWasCalled(x => x.GetBranchById(Arg<int>.Is.Anything));
        }

        [TestMethod]
        public void Details_Action_Calls_GetBranchById_With_Correct_Parameter()
        {
            //Arrange
            int idTestValue = 6;

            _mockService.Expect(x => 
                x.GetBranchById(Arg<int>.Is.Equal(idTestValue))).Return(null);

            //Act
            _controller.Details(idTestValue);

            //Assert (check if id of 6 passed into Details action 
            //then GetById will be also called with id of 6)
            _mockService.VerifyAllExpectations();
        }

        [TestMethod]
        public void Details_Action_Returns_ViewResult()
        {
            //Arrange
            _mockService.Stub(x => x.GetBranchById(Arg<int>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            //Act
            var result = _controller.Details(5);

            //Assert
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

        [TestMethod]
        public void Details_Action_Returns_DefaultView()
        {
            //Arrange
            _mockService.Stub(x => x.GetBranchById(Arg<int>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            //Act
            var result = _controller.Details(5) as ViewResult;

            //Assert
            Assert.AreEqual("", result.ViewName);
        }

        [TestMethod]
        public void Details_Action_Returns_View_With_BranchViewModel()
        {
            //Arrange
            _mockService.Stub(x => x.GetBranchById(Arg<int>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(new BranchViewModel { Id = 5, Code = "aa", Name = "aaa" });

            //Act
            var result = _controller.Details(5) as ViewResult;

            //Assert
            Assert.IsInstanceOfType(result.Model, typeof(BranchViewModel));
        }

        [TestMethod]
        public void Details_Action_Returns_404_If_No_Branch_Found()
        {
            //Arrange
            // null is returned from GetById when a Branch is not found
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(null);

            //Act
            var result = _controller.Details(5);

            //Assert
            Assert.IsInstanceOfType(result, typeof(HttpNotFoundResult));
        }
        #endregion

        #region Create Action Tests
        [TestMethod]
        public void Create_Get_Action_Returns_ViewResult()
        {
            //Arrange
            // no prep beyone TestInitialize needed

            //Act
            var result = _controller.Create();

            //Assert
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

        [TestMethod]
        public void Create_Get_Action_Returns_DefaultView()
        {
            //Arrange
            // no prep beyone TestInitialize needed

            //Act
            var result = _controller.Create() as ViewResult;

            //Assert
            Assert.AreEqual("", result.ViewName);
        }

        [TestMethod]
        public void Create_Post_Action_Returns_ViewResult_When_Invalid()
        {
            //Arrange
            _controller.ViewData.ModelState.Clear();
            _controller.ModelState.AddModelError("Code", "model is invalid");
            var vm = new BranchViewModel();

            //Act
            var result = _controller.Create(vm);

            //Assert
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

        [TestMethod]
        public void Create_Post_Action_Returns_DefaultView_When_Invalid()
        {
            //Arrange
            _controller.ViewData.ModelState.Clear();
            _controller.ModelState.AddModelError("Code", "model is invalid");
            var vm = new BranchViewModel { Id = 0, Code = "", Name = "test" };

            //Act
            var result = _controller.Create(vm) as ViewResult;

            //Assert
            Assert.AreEqual("", result.ViewName);
        }

        [TestMethod]
        public void Create_Post_Action_Returns_Same_Viewmodel_When_Invalid()
        {
            //Arrange
            _controller.ViewData.ModelState.Clear();
            _controller.ModelState.AddModelError("Code", "model is invalid");
            var vm = new BranchViewModel { Id = 0, Code = "", Name = "test" };

            //Act
            var result = _controller.Create(vm) as ViewResult;

            //Assert
            Assert.AreEqual(result.Model, vm);
        }

        [TestMethod]
        public void Create_Post_Action_Calls_Correct_Methods_When_Valid()
        {
            //Arrange
            _mockMapper.Stub(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            _mockService.Stub(x => x.CreateNewBranch(Arg<Branch>.Is.Anything));

            _controller.ViewData.ModelState.Clear();
            var vm = new BranchViewModel { Id = 5, Code = "aa", Name = "aaa" };

            //Act
            _controller.Create(vm);

            //Assert
            _mockService.AssertWasCalled(x => 
                x.CreateNewBranch((Arg<Branch>.Is.Anything)));
            _mockMapper.AssertWasCalled(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything));
        }

        [TestMethod]
        public void Create_Post_Action_Returns_RedirectToAction_When_Valid()
        {
            //Arrange
            _mockMapper.Stub(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            _mockService.Stub(x => x.CreateNewBranch(Arg<Branch>.Is.Anything));

            _controller.ViewData.ModelState.Clear();
            var vm = new BranchViewModel { Id = 5, Code = "aa", Name = "aaa" };

            //Act
            var result = _controller.Create(vm);

            //Assert
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
        }

        [TestMethod]
        public void Create_Post_Action_Returns_Index_When_Valid()
        {
            //Arrange
            _mockMapper.Stub(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            _mockService.Stub(x => x.CreateNewBranch(Arg<Branch>.Is.Anything));

            _controller.ViewData.ModelState.Clear();
            var vm = new BranchViewModel { Id = 5, Code = "aa", Name = "aaa" };

            //Act
            var result = _controller.Create(vm) as RedirectToRouteResult;
            var routeValue = result.RouteValues["action"];

            //Assert
            Assert.AreEqual(routeValue, "Index");
        }
        #endregion

        #region Edit Action Tests
        [TestMethod]
        public void Edit_Get_Action_Calls_BranchService_GetBranchById()
        {
            //Arrange
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(null);

            //Act
            _controller.Edit(1);

            //Assert
            _mockService.AssertWasCalled(x => 
                x.GetBranchById(Arg<int>.Is.Anything));
        }

        [TestMethod]
        public void Edit_Get_Action_Calls_Mapper_If_Branch_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(null);

            //Act
            _controller.Edit(1);

            //Assert
            _mockMapper.AssertWasCalled(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything));
        }

        [TestMethod]
        public void Edit_Get_Action_Returns_ViewResult_If_Branch_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            var branchVm = new BranchViewModel { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(branchVm);

            //Act
            var result = _controller.Edit(1);

            //Assert
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

        [TestMethod]
        public void Edit_Get_Action_Returns_DefaultView_If_Branch_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            var branchVm = new BranchViewModel { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(branchVm);

            //Act
            var result = _controller.Edit(1) as ViewResult;

            //Assert
            Assert.AreEqual("", result.ViewName);
        }

        [TestMethod]
        public void Edit_Get_Action_Returns_Correct_ViewModel_When_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            var branchVm = new BranchViewModel { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(branchVm);

            //Act
            var result = _controller.Edit(1) as ViewResult;

            //Assert
            Assert.AreEqual(result.Model, branchVm);
        }

        [TestMethod]
        public void Edit_Get_Action_Returns_404_If_Branch_Not_Found()
        {
            //Arrange
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(null);

            //Act
            var result = _controller.Edit(1);

            //Assert
            Assert.IsInstanceOfType(result, typeof(HttpNotFoundResult));
        }

        [TestMethod]
        public void Edit_Post_Action_Returns_ViewResult_If_Model_Not_Valid()
        {
            //Arrange
            _controller.ViewData.ModelState.Clear();
            _controller.ModelState.AddModelError("Code", "model is invalid");
            var vm = new BranchViewModel();

            //Act
            var result = _controller.Edit(vm);

            //Assert
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

        [TestMethod]
        public void Edit_Post_Action_Returns_DefaultView_When_Invalid()
        {
            //Arrange
            _controller.ViewData.ModelState.Clear();
            _controller.ModelState.AddModelError("Code", "model is invalid");
            var vm = new BranchViewModel { Id = 0, Code = "", Name = "test" };

            //Act
            var result = _controller.Edit(vm) as ViewResult;

            //Assert
            Assert.AreEqual("", result.ViewName);
        }

        [TestMethod]
        public void Edit_Post_Action_Returns_Same_ViewModel_When_Invalid()
        {
            //Arrange
            _controller.ViewData.ModelState.Clear();
            _controller.ModelState.AddModelError("Code", "model is invalid");
            var vm = new BranchViewModel { Id = 0, Code = "", Name = "test" };

            //Act
            var result = _controller.Edit(vm) as ViewResult;

            //Assert
            Assert.AreEqual(result.Model, vm);
        }

        [TestMethod]
        public void Edit_Post_Action_Calls_Correct_Methods_When_Valid()
        {
            //Arrange
            _mockMapper.Stub(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            _mockService.Stub(x => x.ModifyBranch(Arg<Branch>.Is.Anything));

            _controller.ViewData.ModelState.Clear();

            var vm = new BranchViewModel { Id = 5, Code = "aa", Name = "aaa" };

            //Act
            _controller.Edit(vm);

            //Assert
            _mockService.AssertWasCalled(x => 
                x.ModifyBranch((Arg<Branch>.Is.Anything)));
            _mockMapper.AssertWasCalled(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything));
        }

        [TestMethod]
        public void Edit_Post_Action_Returns_RedirectToAction_When_Valid()
        {
            //Arrange
            _mockMapper.Stub(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            _mockService.Stub(x => x.ModifyBranch(Arg<Branch>.Is.Anything));

            _controller.ViewData.ModelState.Clear();

            var vm = new BranchViewModel { Id = 5, Code = "aa", Name = "aaa" };

            //Act
            var result = _controller.Edit(vm);

            //Assert
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
        }

        [TestMethod]
        public void Edit_Post_Action_Returns_RedirectToAction_Index_When_Valid()
        {
            //Arrange
            _mockMapper.Stub(x => 
                x.Map<BranchViewModel, Branch>(Arg<BranchViewModel>.Is.Anything)).
                Return(new Branch { Id = 5, Code = "aa", Name = "aaa" });

            _mockService.Stub(x => x.ModifyBranch(Arg<Branch>.Is.Anything));

            _controller.ViewData.ModelState.Clear();

            var vm = new BranchViewModel { Id = 5, Code = "aa", Name = "aaa" };

            //Act
            var result = _controller.Edit(vm) as RedirectToRouteResult;
            var routeValue = result.RouteValues["action"];

            //Assert
            Assert.AreEqual(routeValue, "Index");
        }
        #endregion

        #region Delete Action Tests
        [TestMethod]
        public void Delete_Get_Action_Calls_BranchService_GetBranchById()
        {
            //Arrange
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(null);

            //Act
            _controller.Delete(1);

            //Assert
            _mockService.AssertWasCalled(x => 
                x.GetBranchById(Arg<int>.Is.Anything));
        }

        [TestMethod]
        public void Delete_Get_Action_Calls_Mapper_If_Branch_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(null);

            //Act
            _controller.Delete(1);

            //Assert
            _mockMapper.AssertWasCalled(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything));
        }

        [TestMethod]
        public void Delete_Get_Action_Returns_ViewResult_If_Branch_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            var branchVm = new BranchViewModel { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).
                Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(branchVm);

            //Act
            var result = _controller.Delete(1);

            //Assert
            Assert.IsInstanceOfType(result, typeof(ViewResult));
        }

        [TestMethod]
        public void Delete_Get_Action_Returns_DefaultView_If_Branch_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            var branchVm = new BranchViewModel { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).
                Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(branchVm);

            //Act
            var result = _controller.Delete(1) as ViewResult;

            //Assert
            Assert.AreEqual("", result.ViewName);
        }

        [TestMethod]
        public void Delete_Get_Action_Returns_Correct_ViewModel_If_Found()
        {
            //Arrange
            var branchDm = new Branch { Id = 1, Code = "a", Name = "aa" };
            var branchVm = new BranchViewModel { Id = 1, Code = "a", Name = "aa" };
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(branchDm);
            _mockMapper.Stub(x => 
                x.Map<Branch, BranchViewModel>(Arg<Branch>.Is.Anything)).
                Return(branchVm);

            //Act
            var result = _controller.Delete(1) as ViewResult;

            //Assert
            Assert.AreEqual(result.Model, branchVm);
        }

        [TestMethod]
        public void Delete_Get_Action_Returns_404_If_Branch_Not_Found()
        {
            //Arrange
            _mockService.Stub(x => 
                x.GetBranchById(Arg<int>.Is.Anything)).Return(null);

            //Act
            var result = _controller.Delete(1);

            //Assert
            Assert.IsInstanceOfType(result, typeof(HttpNotFoundResult));
        }

        [TestMethod]
        public void Delete_Post_Action_Calls_BranchService_DeleteBranch()
        {
            //Arrange
            _mockService.Stub(x => 
                x.DeleteBranch(Arg<int>.Is.Anything));

            //Act
            _controller.DeleteConfirmed(1);

            //Assert
            _mockService.AssertWasCalled(x => 
                x.DeleteBranch(Arg<int>.Is.Anything));
        }

        [TestMethod]
        public void Delete_Post_Action_Returns_RedirectToAction()
        {
            //Arrange
            _mockService.Stub(x => 
                x.DeleteBranch(Arg<int>.Is.Anything));

            //Act
            var result = _controller.DeleteConfirmed(1);

            //Assert
            Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
        }

        [TestMethod]
        public void Delete_Post_Action_Returns_RedirectToAction_Index()
        {
            //Arrange
            _mockService.Stub(x => 
                x.DeleteBranch(Arg<int>.Is.Anything));

            //Act
            var result = _controller.DeleteConfirmed(1) as RedirectToRouteResult;
            var routeValue = result.RouteValues["action"];

            //Assert
            Assert.AreEqual(routeValue, "Index");
        }
        #endregion
    }
}


ASP.NET MVC3 Using Code First Entity Framework Without Database Generation

Note that this works just as well with MVC4 as it does MVC3.

So, when is Code First not Code First?

It is possible, even recommended, to use 'code first' techniques even when you are not generating the database from the code. This is hinted at in the Creating an Entity Framework Data Model for an ASP.NET MVC Application article on Microsoft's asp.net web site (http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application). The code first technique will mean that you are using POCO classes for the models which are persistence ignorant.

An example to show you how this is achieved...

The first step is to create a SQL database called Bank. I used SQL Express for my test. Then within the database create a table called Customer, you can use the following SQL statement to do this:

CREATE TABLE Customers(
 CustomerID int IDENTITY(1,1) NOT NULL,
 FirstName nvarchar(50) NOT NULL,
 LastName nvarchar(50) NOT NULL,
 Title nvarchar(10) NOT NULL,
 HomePhone nvarchar(20) NULL,
 CONSTRAINT PK_PrivateCustomer PRIMARY KEY CLUSTERED (CustomerID ASC))

Insert a couple of dummy records so we will be able to test we are correctly connected:

INSERT Customers (FirstName, LastName, Title, HomePhone) VALUES ('John', 'Jones', 'Mr', NULL)
INSERT Customers (FirstName, LastName, Title, HomePhone) VALUES ('Steve', 'Smith', 'Mr', '01023123123')

Now create yourself an MVC3 project called Bank in Visual Studio.

Create yourself a BankContext.cs class, I put this in a folder called DAL but you can leave it in the Models folder if you desire. This class should have the following contents:

using System.Data.Entity;

namespace Bank.Models
{
    public class BankContext : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
    }
}

You then need to setup a connection to your database in your web.config, by using convention and calling it BankContext there will be no other code to add, for example:

<add name="BankContext" connectionString="Data Source=.\SQLExpress;Initial Catalog=Bank;Integrated Security=True" providerName="System.Data.SqlClient" />

Create a class in the Model folder called Customer.cs and change it's contents to the following:

namespace Bank.Models
{
    public class Customer
    {
        public int CustomerID { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Title { get; set; }
        public string HomePhone { get; set; }
    }
}


Build the project.

To see the results we need to create a controller and views. Right-click on the Controllers folder and choose to add a new controller. Call the controller 'CustomerController', make it use the template 'Controller with read/write actions and views, using Entity Framework', set the model class to 'Customer (Bank.Models)', and set the data context class to 'BankContext (Bank.Models)'.

Now run the application and once it has started add "/customer" to the end of the url (e.g. http://localhost:21032/customer). You should see the customer index page listing the customers you created earlier in the database.

That is all there is to the basics of using code first techniques with an existing database.

Mapping

The above simple example works flawlessly because we are in full control of the names of the tables, columns, classes and properties, however, in the real world we are likely to have table or column names that do not map nicely to names we want for our classes and properties, and in this case we need to map one to the other.

Microsoft provides two ways of dealing with the problem of having table names different to our class names (column names are also treated in a similar way). We can use Data Annotations where we put attributes in the class and reference System.ComponentModel.DataAnnotations, or we can use the Fluent API.

The use of Data Annotations has the advantage of keeping everything together and is in some ways easier to understand and maintain, but it has the disadvantage of introducing a dependency that means we no longer are using true POCOs.

This is what our class might look like if we were to use Data Annotations to specify the table name to use:

using System.ComponentModel.DataAnnotations;

namespace Bank.Models
{
    [Table("Customers")]
    public class Customer
    {
        public int CustomerID { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Title { get; set; }
        public string HomePhone { get; set; }
    }
}


We can achieve the same results using the Fluent API, it takes more code but it means we maintain our POCOs in a pure state. The first step is to create a mapping class for each model to map between the model and the database, in this case we will create a CustomerMap class that looks like this:

using System.Data.Entity.ModelConfiguration;

public class CustomerMap : EntityTypeConfiguration<Customer>
{
    public CustomerMap()
    {
        ToTable("Customers");
    }
}


We also need to make a change to the DbContext class so that OnModelCreating will load our mapping.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new CustomerMap());
}


The Fluent API is very flexible and allows you to achieve much more than simply defining the mapping between table/column names and class/property names.

Validation

Model validation such as defining the maximum length for string properties can also be achieved using Data Annotations or the Fluent API. Use of Data Annotations for validation properties doesn't break the POCOs in the same way as it does for database mappings but gives you an enriched model that is understood by the views when validating user input. You may of course prefer to use view models to add a further layer of separation and keep your POCOs clean.

Further Information

Jon Galloway in one of his blogs (Generating EF Code First model classes from an existing database - http://weblogs.asp.net/jgalloway/archive/2011/02.aspx) suggests an easy way of generating the POCOs using a Microsoft tool, the EF 4.x DbContext Generator. I have tried this 'Microsoft way' and it works pretty well.

A tool called ST4bby has been recommended to me that does the same thing, I have not used this myself.

Adam Nelson suggested that by using the "Entity Framework Power Tools" you get the attributes for free. This is a great tool that builds the Fluent API mappings for you by referencing an existing database. It does not yet allow you to only generate part of a database or use Data Annotations rather than the Fluent API.



Extract the first N words from a string with C#

This code will return the first 5 words, change the number in the regular expression as needed:

string testString = "The quick brown fox jumps over the lazy dog."
string firstWords = Regex.Match(testString, @"^(\w+\b.*?){5}").ToString();



Streaming Files for more Secure Downloads in ASP.NET

If you just have a link to a file on your web site then you maybe leaving yourself open to other sites linking to the same files thereby giving their users the benefit of content without any hit on their bandwidth. It will also give clues to your site structure that can only be of benefit to anyone wishing to compromise your site's security.

One workaround to this is to stream the files to your users using a FileStream and the Response object. Here is some C# code that will do that job for you:

/// <summary>
/// Write a secure file out to the response stream. Writes piece-meal in 4K chunks to
/// help prevent problems with large files.
/// <example>
/// <code>WriteFileToResponse(@"secureFolder/mysecurefile.pdf", @"test.pdf",
/// @"application/pdf");</code>
/// </example>
/// <example>
/// <code>WriteFileToResponse(@"secureFolder/mysecurefile.pdf", @"test.pdf");</code>
/// </example>
/// </summary>
/// <param name="secureFilePath">>Relative path to the file to download from our
/// secure folder</param>
/// <param name="userFilename">Name of file the user will see</param>
/// <param name="contentType">MIME type of the file for Response.ContentType,
/// "application/octet-stream" is a good catch all. A list of other possible values
/// can be found at http://msdn.microsoft.com/en-us/library/ms775147.aspx </param>

public void WriteFileToResponse(string secureFilePath, string userFilename,
    string contentType = @"application/octet-stream")
{
    // Process the file in 4K blocks
    byte[] dataBlock = new byte[0x1000];
    long fileSize;
    int bytesRead;
    long totalBytesRead = 0;

    using (var fs = new FileStream(Server.MapPath(secureFilePath),
        FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        fileSize = fs.Length;

        Response.Clear();
        Response.ContentType = contentType;
        Response.AddHeader("Content-Disposition",
            "attachment; filename=" + userFilename);

        while (totalBytesRead < fileSize)
        {
            if (!Response.IsClientConnected)
                break;

            bytesRead = fs.Read(dataBlock, 0, dataBlock.Length);
            Response.OutputStream.Write(dataBlock, 0, bytesRead);
            Response.Flush();
            totalBytesRead += bytesRead;
        }

        Response.Close();
    }
}



New article: Using the Entity Framework and the ObjectDataSource: Custom Paging

A new article is up that extends the Microsoft tutorial on using the Entity Framework with the ObjectDataSource to include custom paging. It can be found by following this link: Using the Entity Framework and the ObjectDataSource: Custom Paging.

 



Using data from Entity Framework 2 to fill a 2010 local SSRS report in ASP.NET

When you design a local SSRS report you are forced to use a Dataset as part of the design process, however, this does not mean that you have to keep the dependancy on a dataset or even retain the dataset in your project once you have completed the design.

Simply use code similar to the C# example that follows to clear the dataset the report is expecting to use and specify the new collection of data it is to use instead:

var context = new AWEntities();

var vendors = from v in context.Vendors
                    where v.CreditRating != 1
                    select v;

ReportViewer1.LocalReport.DataSources.Clear();
ReportDataSource datasource = new ReportDataSource("VendorList", vendors);
ReportViewer1.LocalReport.DataSources.Add(datasource);
ReportViewer1.LocalReport.Refresh();


You can use the same method to substitute data from Linq to SQL or ADO.NET if they are your DAL technology of choice.

 



New article: Setting SSRS Report Parameters from ASP.NET C# Code

A new article is up that discusses how to pass values to SSRS reports at runtime from web forms. Please find it here.



New article regarding the Back Button displaying pages after Logout in ASP.NET

A new article is up that discusses a workaround to the problem of the user pressing the Back button in their browser after they have logged out and an application page being displayed that should require authentication first. Please find it here.



New article on preventing multiple concurrent use in asp.net

I have posted up an article today entitled Preventing a User From Having Multiple Concurrent Sessions