Here in this article I will explain what Unit Testing is in MVC projects. When MVC was launched Unit Testing was promoted as one of the biggest advantages of using MVC when developing business applications. If your business application is growing day by day then it becomes challenging to keep the application on track. Then Unit Testing plays a vital role in the success of your business application.
Now we will learn Unit Testing step-by-step. Here I will create a MVC application first with Entity Framework to do CRUD operations.
Open Visual Studio 2012 -> New -> Project.
Now your solution will look as in the following.
Now right-click on the Model Folder then select Add -> ADO.NET Entity Data Model.
Here we will use the repository pattern so right-click on the Model Folder then select Add New Interface.
Define the following method in IEmployeeRepository.cs.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace UnitTestingAppInMVC.Models
- {
- public interface IEmployeeRepository : IDisposable
- {
- IEnumerable<Employee> GetAllEmployee();
- Employee GetEmployeeByID(int emp_ID);
- void InsertEmployee(Employee emp);
- void DeleteEmployee(int emp_ID);
- void UpdateEmployee(Employee emp);
- int Save();
- }
- }
Now again right-click on the Model Folder then select Add New Class.
EmployeeRepository.cs is
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Linq;
- using System.Web;
-
- namespace UnitTestingAppInMVC.Models
- {
- public class EmployeeRepository: IEmployeeRepository,
- IDisposable
- {
-
- EmployeeManagementEntities context = new EmployeeManagementEntities();
-
- public IEnumerable < Employee > GetAllEmployee()
- {
- return context.Employee.ToList();
- }
-
- public Employee GetEmployeeByID(int id)
- {
- return context.Employee.Find(id);
- }
-
- public void InsertEmployee(Employee emp)
- {
- context.Employee.Add(emp);
- }
-
- public void DeleteEmployee(int emp_ID)
- {
- Employee emp = context.Employee.Find(emp_ID);
- context.Employee.Remove(emp);
- }
-
- public void UpdateEmployee(Employee emp)
- {
- context.Entry(emp).State = EntityState.Modified;
- }
-
- public int Save()
- {
- return context.SaveChanges();
- }
-
- private bool disposed = false;
-
- protected virtual void Dispose(bool disposing)
- {
- if (!this.disposed)
- {
- if (disposing)
- {
- context.Dispose();
- }
- }
- this.disposed = true;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- }
- }
Now Right-click on the Controller then select Add New Controller.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using UnitTestingAppInMVC.Models;
- using PagedList;
- using System.Data;
-
- namespace UnitTestingAppInMVC.Controllers
- {
- public class EmployeeController: Controller
- {
- IEmployeeRepository employeeRepository;
-
- public EmployeeController(): this(new EmployeeRepository()) {}
-
- public EmployeeController(IEmployeeRepository repository)
- {
- employeeRepository = repository;
- }
-
- public ViewResult Index(string sortOrder, string currentFilter, string searchString, int ? page)
- {
- ViewData["ControllerName"] = this.ToString();
- ViewBag.CurrentSort = sortOrder;
- ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Emp_ID" : "";
-
- if (searchString != null)
- {
- page = 1;
- } else {
- searchString = currentFilter;
- }
- ViewBag.CurrentFilter = searchString;
-
- var employees = from s in employeeRepository.GetAllEmployee()
- select s;
- if (!String.IsNullOrEmpty(searchString))
- {
- employees = employees.Where(s = > s.Name.ToUpper().Contains(searchString.ToUpper()) || s.Name.ToUpper().Contains(searchString.ToUpper()));
- }
- switch (sortOrder) {
- case "Emp ID":
- employees = employees.OrderByDescending(s = > s.Emp_ID);
- break;
- case "Name":
- employees = employees.OrderBy(s = > s.Name);
- break;
- case "State":
- employees = employees.OrderByDescending(s = > s.State);
- break;
- case "Country":
- employees = employees.OrderByDescending(s = > s.Country);
- break;
- default:
- employees = employees.OrderBy(s = > s.Emp_ID);
- break;
- }
-
- int pageSize = 5;
- int pageNumber = (page ? ? 1);
- return View("Index", employees.ToPagedList(pageNumber, pageSize));
- }
-
-
-
-
- public ViewResult Details(int id)
- {
- Employee emp = employeeRepository.GetEmployeeByID(id);
- return View(emp);
- }
-
-
-
-
- public ActionResult Create()
- {
- return View("Create");
- }
-
-
-
-
- [HttpPost]
- public ActionResult Create(Employee emp)
- {
- try {
- if (ModelState.IsValid)
- {
- employeeRepository.InsertEmployee(emp);
- employeeRepository.Save();
- return RedirectToAction("Index");
- }
- }
- catch (Exception ex)
- {
- ModelState.AddModelError(string.Empty, "Some Error Occured.");
- }
- return View("Create", emp);
- }
-
-
-
-
- public ActionResult Edit(int id)
- {
- Employee emp = employeeRepository.GetEmployeeByID(id);
- return View(emp);
- }
-
-
-
-
- [HttpPost]
- [ValidateAntiForgeryToken]
- public ActionResult Edit(Employee emp)
- {
- try
- {
- if (ModelState.IsValid)
- {
- employeeRepository.UpdateEmployee(emp);
- employeeRepository.Save();
- return RedirectToAction("Index");
- }
- }
- catch (Exception ex)
- {
- ModelState.AddModelError(string.Empty, "Some error Occured.");
- }
- return View(emp);
- }
-
-
-
-
- public ActionResult Delete(bool ? saveChangesError = false, int id = 0)
- {
- if (saveChangesError.GetValueOrDefault())
- {
- ViewBag.ErrorMessage = "Some Error Occured.";
- }
- Employee emp = employeeRepository.GetEmployeeByID(id);
- return View(emp);
- }
-
-
-
-
- [HttpPost]
- [ValidateAntiForgeryToken]
- public ActionResult Delete(int id)
- {
- try
- {
- Employee emp = employeeRepository.GetEmployeeByID(id);
- employeeRepository.DeleteEmployee(id);
- employeeRepository.Save();
- }
- catch (Exception ex)
- {
- return RedirectToAction("Delete", new
- {
- id = id, saveChangesError = true
- });
- }
- return RedirectToAction("Index");
- }
-
- protected override void Dispose(bool disposing)
- {
- employeeRepository.Dispose();
- base.Dispose(disposing);
- }
-
- }
- }
Now add a View for the Index, Detail, Edit and Delete Action methods.
Unit Testing
Now to work on the Unit Testing part.
In UnitTestingAppInMVC.Tests add a folder named Model. Right-click on the Model Folder then select Add New Class InMemoryEmployeeRepository.cs.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using UnitTestingAppInMVC.Models;
-
- namespace UnitTestingAppInMVC.Tests.Models
- {
- class InMemoryEmployeeRepository: IEmployeeRepository
- {
- private List < Employee > _db = new List < Employee > ();
-
- public Exception ExceptionToThrow
- {
- get;
- set;
- }
-
- public IEnumerable < Employee > GetAllEmployee()
- {
- return _db.ToList();
- }
-
- public Employee GetEmployeeByID(int id)
- {
- return _db.FirstOrDefault(d = > d.Emp_ID == id);
- }
-
- public void InsertEmployee(Employee employeeToCreate)
- {
-
-
- _db.Add(employeeToCreate);
- }
-
- public void DeleteEmployee(int id)
- {
- _db.Remove(GetEmployeeByID(id));
- }
-
-
- public void UpdateEmployee(Employee employeeToUpdate)
- {
-
- foreach(Employee employee in _db)
- {
- if (employee.Emp_ID == employeeToUpdate.Emp_ID)
- {
- _db.Remove(employee);
- _db.Add(employeeToUpdate);
- break;
- }
- }
- }
-
- public int Save()
- {
- return 1;
- }
-
-
- private bool disposed = false;
-
- protected virtual void Dispose(bool disposing)
- {
- if (!this.disposed)
- {
- if (disposing)
- {
-
- }
- }
- this.disposed = true;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- }
- }
Add A New Controller class
Now add a new Controller class EmployeeControllerTest.cs.
Here define all your action methods as Test Methods for which you want to do Unit Testing.
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Security.Principal;
- using System.Text;
- using System.Threading.Tasks;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.Routing;
- using UnitTestingAppInMVC.Controllers;
- using UnitTestingAppInMVC.Models;
- using UnitTestingAppInMVC.Tests.Models;
-
- namespace UnitTestingAppInMVC.Tests.Controllers
- {
- [TestClass]
- public class EmployeeControllerTest
- {
-
-
-
- [TestMethod]
- public void IndexView()
- {
- var empcontroller = GetEmployeeController(new InMemoryEmployeeRepository());
-
- ViewResult result = empcontroller.Index(null, null, null, null);
- Assert.AreEqual("Index", result.ViewName);
- Assert.IsInstanceOfType(result, typeof(ViewResult));
- }
-
-
-
-
-
-
- private static EmployeeController GetEmployeeController(IEmployeeRepository emprepository)
- {
- EmployeeController empcontroller = new EmployeeController(emprepository);
- empcontroller.ControllerContext = new ControllerContext()
- {
- Controller = empcontroller,
- RequestContext = new RequestContext(new MockHttpContext(), new RouteData())
- };
- return empcontroller;
- }
-
-
-
-
- [TestMethod]
- public void GetAllEmployeeFromRepository()
- {
-
- Employee employee1 = GetEmployeeName(1, "Rahul Saxena", "[email protected]", "Software Developer", "Noida", "Uttar Pradesh", "India");
- Employee employee2 = GetEmployeeName(2, "Abhishek Saxena", "[email protected]", "Tester", "Saharanpur", "Uttar Pradesh", "India");
- InMemoryEmployeeRepository emprepository = new InMemoryEmployeeRepository();
- emprepository.InsertEmployee(employee1);
- emprepository.InsertEmployee(employee2);
- var controller = GetEmployeeController(emprepository);
- var result = controller.Index(null, null, null, null);
- var datamodel = (IEnumerable < Employee > ) result.ViewData.Model;
- CollectionAssert.Contains(datamodel.ToList(), employee1);
- CollectionAssert.Contains(datamodel.ToList(), employee2);
- }
-
-
-
-
-
-
-
-
-
-
-
-
- Employee GetEmployeeName(int Emp_ID, string Name, string Email, string Designation, string City, string State, string Country)
- {
- return new Employee
- {
- Emp_ID = Emp_ID,
- Name = Name,
- Email = Email,
- Designation = Designation,
- City = City,
- State = State,
- Country = Country
- };
- }
-
-
-
-
-
- [TestMethod]
- public void Create_PostEmployeeInRepository()
- {
- InMemoryEmployeeRepository emprepository = new InMemoryEmployeeRepository();
- EmployeeController empcontroller = GetEmployeeController(emprepository);
- Employee employee = GetEmployeeID();
- empcontroller.Create(employee);
- IEnumerable < Employee > employees = emprepository.GetAllEmployee();
- Assert.IsTrue(employees.Contains(employee));
- }
-
-
-
-
-
- Employee GetEmployeeID()
- {
- return GetEmployeeName(1, "Rahul Saxena", "[email protected]", "Software Developer", "Noida", "Uttar Pradesh", "India");
- }
-
-
-
-
- [TestMethod]
- public void Create_PostRedirectOnSuccess()
- {
- EmployeeController controller = GetEmployeeController(new InMemoryEmployeeRepository());
- Employee model = GetEmployeeID();
- var result = (RedirectToRouteResult) controller.Create(model);
- Assert.AreEqual("Index", result.RouteValues["action"]);
- }
-
-
-
-
- [TestMethod]
- public void ViewIsNotValid()
- {
- EmployeeController empcontroller = GetEmployeeController(new InMemoryEmployeeRepository());
- empcontroller.ModelState.AddModelError("", "mock error message");
- Employee model = GetEmployeeName(1, "", "", "", "", "", "");
- var result = (ViewResult) empcontroller.Create(model);
- Assert.AreEqual("Create", result.ViewName);
- }
- }
-
- public class MockHttpContext: HttpContextBase
- {
- private readonly IPrincipal _user = new GenericPrincipal(new GenericIdentity("someUser"), null );
-
- public override IPrincipal User
- {
- get
- {
- return _user;
- }
- set
- {
- base.User = value;
- }
- }
- }
- }
Now run your test methods.
See your Test Run result here.