Unit Testing in MVC 4 Using Entity Framework

This article is a brief introduction to the use of unit testing in MVC 4 using Entity Framework with Repository Pattern.

What is Unit testing: The basic purpose of unit testing is to test the methods of business logic of ASP.NET or MVC projects.

What is Entity Framework

Entity Framework (EF) is an object-relational mapper that enables .NET developers to work with relational data using domain-specific objects. It eliminates the need for most of the data-access code that developers usually need to write. For more see: http://msdn.microsoft.com/en-us/data/ef.aspx

What is Repository Pattern in MVC

The Repository Pattern is useful for decoupling entity operations from presentation, which allows easy mocking and unit testing.

"The Repository will delegate to the appropriate infrastructure services to get the job done. Encapsulating in the mechanisms of storage, retrieval and query is the most basic feature of a Repository implementation" .... "Most common queries should also be hardcoded to the Repositories as methods." For more see: http://webmasterdriver.blogspot.in/2012/05/what-is-repository-pattern-in-aspnet.html

Getting Started

Create a new Project. Open Visual Studio 2012.

Go to "File" => "New" => "Project...".

Select "Web" in installed templates.

Select "ASP.NET MVC 4 Web Application".

Enter the Name and choose the location.

Click "OK".

Img1.jpg

Image 1.

After clicking the OK button in the next wizard there is a check box for creating a unit test project. If you want to create a unit test project then enable that check box, you can add it later or also add a new item feature.

img2.jpg

Image 2.

img3.jpg

Image 3.

First of all we are going to add a new ADO.NET Entity Data Model and provide it a relevant name.

img4.jpg

Image 4.

In the next wizard select "Generate from database" and click Next and create a new connection and select a database name. In my sample I am making a sqlexpress connection and my database is located in the App_Data folder. The attached sample has a NORTHWND database.

img5.jpg

Image 5.

img6.jpg

Image 6.

img7.jpg

Image 7.

img8.jpg

Image 8.

img9.jpg

Image 9.

img10.jpg

Image 10.

As you can see the connection string has been added to the web.config file:

  1. <connectionStrings>  
  2. <add name="NORTHWNDEntities" connectionString="metadata=res://*/Models.NORHWNDModel.csdl|res://*/Models.NORHWNDModel.ssdl|res://*/Models.NORHWNDModel.msl;provider=System.Data.SqlClient;provider connection string="data source=(LocalDB)\v11.0;attachdbfilename=|DataDirectory|\NORTHWND.MDF;integrated security=True;connect timeout=30;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" /> </connectionStrings>
img11.jpg

Image 11.

Now add a new class in models "EmployeeRepository" and start working on it. The following is my code:

  1. public class EmployeeRepository : IEmployeeRepository  
  2. {  
  3.     /// <summary>  
  4.     /// Northwnd entity object  
  5.     /// </summary>  
  6.     private NORTHWNDEntities _db = new NORTHWNDEntities();  
  7.     /// <summary>  
  8.     /// This method used to get all employee listing  
  9.     /// </summary>  
  10.     /// <returns></returns>  
  11.     public IEnumerable<Employee> GetAllEmployee()  
  12.     {  
  13.         return _db.Employees.ToList();  
  14.     }  
  15.     /// <summary>  
  16.     /// This method used to filter employee by id  
  17.     /// </summary>  
  18.     /// <param name="id"></param>  
  19.     /// <returns></returns>  
  20.     public Employee GetEmployeeByID(int id)  
  21.     {  
  22.         return _db.Employees.FirstOrDefault(d => d.EmployeeID == id);  
  23.     }  
  24.     /// <summary>  
  25.     /// This method is used to create new employee  
  26.     /// </summary>  
  27.     /// <param name="contactToCreate"></param>  
  28.     public void CreateNewEmployee(Employee employeeToCreate)  
  29.     {  
  30.         _db.Employees.Add(employeeToCreate);  
  31.         _db.SaveChanges();  
  32.         //   return contactToCreate;  
  33.     }  
  34.     /// <summary>  
  35.     ///  Save chages functions  
  36.     /// </summary>  
  37.     /// <returns></returns>  
  38.     public int SaveChanges()  
  39.     {  
  40.         return _db.SaveChanges();  
  41.     }  
  42.     /// <summary>  
  43.     ///  This method used to delete employee  
  44.     /// </summary>  
  45.     /// <param name="id"></param>  
  46.     public void DeleteEmployee(int id)  
  47.     {  
  48.         var conToDel = GetEmployeeByID(id);  
  49.         _db.Employees.Remove(conToDel);  
  50.         _db.SaveChanges();  
  51.     }  
  52. } 

As you can see, this class is inheriting an IEmployeeRepository interface. Now add a new interface.

  1. public interface IEmployeeRepository  
  2. {  
  3.   IEnumerable<Employee> GetAllEmployee();  
  4.   void CreateNewEmployee(Employee employeeToCreate);  
  5.   void DeleteEmployee(int id);  
  6.   Employee GetEmployeeByID(int id);         
  7.   int SaveChanges();  
  8. } 

We are good so far. It is now time to add a new controller in the Controllers directory.

img12.jpg

Image 12.

My controller name is EmployeeController; see:

  1. public class EmployeeController : Controller  
  2. {  
  3.     IEmployeeRepository _repository;  
  4.     public EmployeeController() : this(new EmployeeRepository()) { }  
  5.     public EmployeeController(IEmployeeRepository repository)  
  6.     {  
  7.         _repository = repository;  
  8.     }  
  9.     ////  
  10.     //// GET: /Employee/  
  11.     public ViewResult Index()  
  12.     {  
  13.         ViewData["ControllerName"] = this.ToString();  
  14.         return View("Index", _repository.GetAllEmployee());  
  15.     }  
  16.     ////  
  17.     //// GET: /Employee/Details/5  
  18.     public ActionResult Details(int id = 0)  
  19.     {  
  20.         //int idx = id.HasValue ? (int)id : 0;  
  21.         Employee cnt = _repository.GetEmployeeByID(id);  
  22.         return View("Details", cnt);  
  23.     }  
  24.     //  
  25.     // GET: /Employee/Create  
  26.     public ActionResult Create()  
  27.     {  
  28.         return View("Create");  
  29.     }  
  30.     //  
  31.     // POST: /Employee/Create  
  32.     [HttpPost]  
  33.     public ActionResult Create([Bind(Exclude = "Id")] Employee employeeToCreate)  
  34.     {  
  35.         try  
  36.         {  
  37.             if (ModelState.IsValid)  
  38.             {  
  39.                 _repository.CreateNewEmployee(employeeToCreate);  
  40.                 return RedirectToAction("Index");  
  41.             }  
  42.         }  
  43.         catch (Exception ex)  
  44.         {  
  45.             ModelState.AddModelError("", ex);  
  46.             ViewData["CreateError"] = "Unable to create; view innerexception";  
  47.         }  
  48.         return View("Create");  
  49.     }  
  50.     //  
  51.     // GET: /Employee/Edit/5  
  52.     public ActionResult Edit(int id = 0)  
  53.     {  
  54.         var employeeToEdit = _repository.GetEmployeeByID(id);  
  55.         return View(employeeToEdit);  
  56.     }  
  57.     //  
  58.     // GET: /Employee/Edit/5  
  59.     [HttpPost]  
  60.     public ActionResult Edit(int id, FormCollection collection)  
  61.     {  
  62.         Employee cnt = _repository.GetEmployeeByID(id);  
  63.         try  
  64.         {  
  65.             if (TryUpdateModel(cnt))  
  66.             {  
  67.                 _repository.SaveChanges();  
  68.                 return RedirectToAction("Index");  
  69.             }  
  70.         }  
  71.         catch (Exception ex)  
  72.         {  
  73.             if (ex.InnerException != null)  
  74.                 ViewData["EditError"] = ex.InnerException.ToString();  
  75.             else  
  76.                 ViewData["EditError"] = ex.ToString();  
  77.         }  
  78.         #if DEBUG  
  79.         foreach (var modelState in ModelState.Values)  
  80.         {  
  81.             foreach (var error in modelState.Errors)  
  82.             {  
  83.                 if (error.Exception != null)  
  84.                 {  
  85.                     throw modelState.Errors[0].Exception;  
  86.                 }  
  87.             }  
  88.         }  
  89.         #endif  
  90.         return View(cnt);  
  91.     }  
  92.     //  
  93.     // GET: /Employee/Delete/5  
  94.     public ActionResult Delete(int id)  
  95.     {  
  96.         var conToDel = _repository.GetEmployeeByID(id);  
  97.         return View(conToDel);  
  98.     }  
  99.     //  
  100.     // POST: /Employee/Delete/5  
  101.     [HttpPost]  
  102.     public ActionResult Delete(int id, FormCollection collection)  
  103.     {  
  104.         try  
  105.         {  
  106.             _repository.DeleteEmployee(id);  
  107.             return RedirectToAction("Index");  
  108.         }  
  109.         catch  
  110.         {  
  111.             return View();  
  112.         }  
  113.     }

Now add Views by right-clicking on ViewResult in the controller and select strongly typed view and select model class which is created by a data model and select scaffold template and click Add.

img13.jpg

Image 13.

img14.jpg

Image 14.

You can add views in the same way for Create, Delete, Details. Edit, Empty and List scaffold templates.

Now let's run the application to see the result.

The Index view will show you the listing of all employees:

img15.jpg

Image 15.

Click on create link that will create new employees:

img16.jpg

Image 16.

To edit an existing employee, click on the edit link:

img17.jpg

Image 17.

To see the details of an employee click on the details link:

img18.jpg

Image 18.

To delete an employee's record click on the delete botton:

img19.jpg

Image 19.

We are now good with the MVC application using a repository and the Entity Framework.

Now it is time to work on Unit Testing. First of all create a models directory in the test project. As you will see, the MVC project library was added to the test project. If you are adding a new test project to the solution then you have to add the library manually.

Tests/Models/InMemoryEmployeeRepository.cs

First of all add a namespace for this class, as in:

  1. using UnitTestingInMVCUsingEF.Models;  
  2. class InMemoryEmployeeRepository : IEmployeeRepository  
  3. {  
  4.     private List<Employee> _db = new List<Employee>();  
  5.     public Exception ExceptionToThrow { getset; }  
  6.     public IEnumerable<Employee> GetAllEmployee()  
  7.     {  
  8.         return _db.ToList();  
  9.     }  
  10.     public Employee GetEmployeeByID(int id)  
  11.     {  
  12.         return _db.FirstOrDefault(d => d.EmployeeID == id);  
  13.     }  
  14.     public void CreateNewEmployee(Employee employeeToCreate)  
  15.     {  
  16.         if (ExceptionToThrow != null)  
  17.             throw ExceptionToThrow;  
  18.         _db.Add(employeeToCreate);  
  19.     }  
  20.     public void SaveChanges(Employee employeeToUpdate)  
  21.     {  
  22.         foreach (Employee employee in _db)  
  23.         {  
  24.             if (employee.EmployeeID == employeeToUpdate.EmployeeID)  
  25.             {  
  26.                 _db.Remove(employee);  
  27.                 _db.Add(employeeToUpdate);  
  28.                 break;  
  29.             }  
  30.         }  
  31.     }  
  32.     public void Add(Employee employeeToAdd)  
  33.     {  
  34.         _db.Add(employeeToAdd);  
  35.     }  
  36.     public int SaveChanges()  
  37.     {  
  38.         return 1;  
  39.     }  
  40.     public void DeleteEmployee(int id)  
  41.     {  
  42.         _db.Remove(GetEmployeeByID(id));  
  43.     }  
  44. } 

Now let's add a new controller in the Tests/Controllers/ EmployeeControllerTest class, as in:

  1. using UnitTestingInMVCUsingEF.Models;  
  2. using UnitTestingInMVCUsingEF.Controllers;  
  3. using UnitTestingInMVCUsingEF.Tests.Models;  
  4. [TestClass]  
  5. public class EmployeeControllerTest  
  6. {  
  7.     /// <summary>  
  8.     /// This method used for index view  
  9.     /// </summary>  
  10.     [TestMethod]  
  11.     public void IndexView()  
  12.     {  
  13.         var empcontroller = GetEmployeeController(new InMemoryEmployeeRepository());  
  14.         ViewResult result = empcontroller.Index();             
  15.         Assert.AreEqual("Index", result.ViewName);  
  16.     }  
  17.     /// <summary>  
  18.     /// This method used to get employee controller  
  19.     /// </summary>  
  20.     /// <param name="repository"></param>  
  21.     /// <returns></returns>  
  22.     private static EmployeeController GetEmployeeController(IEmployeeRepository emprepository)  
  23.     {  
  24.         EmployeeController empcontroller = new EmployeeController(emprepository);  
  25.         empcontroller.ControllerContext = new ControllerContext()  
  26.         {  
  27.             Controller = empcontroller,  
  28.             RequestContext = new RequestContext(new MockHttpContext(), new RouteData())  
  29.         };  
  30.         return empcontroller;  
  31.     }  
  32.     /// <summary>  
  33.     ///  This method used to get all employye listing  
  34.     /// </summary>  
  35.     [TestMethod]  
  36.     public void GetAllEmployeeFromRepository()  
  37.     {  
  38.         // Arrange  
  39.         Employee employee1 = GetEmployeeName(1, "Beniwal""Raj""Mr""H33""Noida""U.P""201301");  
  40.         Employee employee2 = GetEmployeeName(2, "Beniwal""Pari""Ms""d77""Noida""U.P""201301");  
  41.         InMemoryEmployeeRepository emprepository = new InMemoryEmployeeRepository();  
  42.         emprepository.Add(employee1);  
  43.         emprepository.Add(employee2);  
  44.         var controller = GetEmployeeController(emprepository);  
  45.         var result = controller.Index();  
  46.         var datamodel = (IEnumerable<Employee>)result.ViewData.Model;  
  47.         CollectionAssert.Contains(datamodel.ToList(), employee1);  
  48.         CollectionAssert.Contains(datamodel.ToList(), employee2);  
  49.     }  
  50.     /// <summary>  
  51.     /// This method used to get emp name  
  52.     /// </summary>  
  53.     /// <param name="id"></param>  
  54.     /// <param name="lName"></param>  
  55.     /// <param name="fName"></param>  
  56.     /// <param name="title"></param>  
  57.     /// <param name="address"></param>  
  58.     /// <param name="city"></param>  
  59.     /// <param name="region"></param>  
  60.     /// <param name="postalCode"></param>  
  61.     /// <returns></returns>  
  62.     Employee GetEmployeeName(int id, string lName, string fName, string title, string address, string city, string region, string postalCode)  
  63.     {  
  64.         return new Employee  
  65.         {  
  66.             EmployeeID = id,  
  67.             LastName = lName,  
  68.             FirstName = fName,  
  69.             Title = title,  
  70.             Address = address,  
  71.             City = city,  
  72.             Region = region,  
  73.             PostalCode = postalCode  
  74.         };  
  75.     }  
  76.     /// <summary>  
  77.     /// This test method used to post employee  
  78.     /// </summary>  
  79.     [TestMethod]  
  80.     public void Create_PostEmployeeInRepository()  
  81.     {  
  82.         InMemoryEmployeeRepository emprepository = new InMemoryEmployeeRepository();  
  83.         EmployeeController empcontroller = GetEmployeeController(emprepository);  
  84.         Employee employee = GetEmployeeID();  
  85.         empcontroller.Create(employee);  
  86.         IEnumerable<Employee> employees = emprepository.GetAllEmployee();  
  87.         Assert.IsTrue(employees.Contains(employee));  
  88.     }  
  89.     /// <summary>  
  90.     ///  
  91.     /// </summary>  
  92.     /// <returns></returns>  
  93.     Employee GetEmployeeID()  
  94.     {  
  95.         return GetEmployeeName(1, "Beniwal""Raj""Mr""H33""Noida""U.P""201301");  
  96.     }  
  97.     /// <summary>  
  98.     ///  
  99.     /// </summary>  
  100.     [TestMethod]  
  101.     public void Create_PostRedirectOnSuccess()  
  102.     {             
  103.         EmployeeController controller = GetEmployeeController(new InMemoryEmployeeRepository());  
  104.         Employee model = GetEmployeeID();  
  105.         var result = (RedirectToRouteResult)controller.Create(model);  
  106.         Assert.AreEqual("Index", result.RouteValues["action"]);  
  107.     }  
  108.     /// <summary>  
  109.     ///  
  110.     /// </summary>  
  111.     [TestMethod]  
  112.     public void ViewIsNotValid()  
  113.     {             
  114.         EmployeeController empcontroller = GetEmployeeController(new InMemoryEmployeeRepository());             
  115.         empcontroller.ModelState.AddModelError("""mock error message");  
  116.         Employee model = GetEmployeeName(1, """""""","","","");  
  117.         var result = (ViewResult)empcontroller.Create(model);  
  118.         Assert.AreEqual("Create", result.ViewName);  
  119.     }  
  120.     /// <summary>  
  121.     ///  
  122.     /// </summary>  
  123.     [TestMethod]  
  124.     public void RepositoryThrowsException()  
  125.     {  
  126.         // Arrange  
  127.         InMemoryEmployeeRepository emprepository = new InMemoryEmployeeRepository();  
  128.         Exception exception = new Exception();  
  129.         emprepository.ExceptionToThrow = exception;  
  130.         EmployeeController controller = GetEmployeeController(emprepository);  
  131.         Employee employee = GetEmployeeID();  
  132.         var result = (ViewResult)controller.Create(employee);  
  133.         Assert.AreEqual("Create", result.ViewName);  
  134.         ModelState modelState = result.ViewData.ModelState[""];  
  135.         Assert.IsNotNull(modelState);  
  136.         Assert.IsTrue(modelState.Errors.Any());  
  137.         Assert.AreEqual(exception, modelState.Errors[0].Exception);  
  138.     }  
  139.     private class MockHttpContext : HttpContextBase  
  140.     {  
  141.         private readonly IPrincipal _user = new GenericPrincipal(new GenericIdentity("someUser"), null /* roles */);  
  142.         public override IPrincipal User  
  143.         {  
  144.             get  
  145.             {  
  146.                 return _user;  
  147.             }  
  148.             set  
  149.             {  
  150.                 base.User = value;  
  151.             }  
  152.         }  
  153.     }  
  154. }
It is now time to run the test cases.

img20.jpg

Image 20.

img21.jpg

Image 21.

As you will see all test cases are the result.