Unit Testing در پروژه های MVC

در این مقاله به مبحث Unit Testing در پروژه های MVC خواهیم پرداخت

Unit Testing در پروژه های MVC

در این مقاله به مبحث Unit Testing در پروژه های  MVC خواهیم پرداخت

وقتی MVC راه اندازی شد Unit Testing به عنوان یکی از بزرگترین مزایای استفاده از MVC در زمان توسعه برنامه های تجاری پیشنهاد شد.اگر نرم افزار تجاری شما روز به روز در حال رشد است برای نگه داشتن برنامه در مسیر نیاز به چالش کشیدن آن است اینجاست که Unit testing  نقش حیاتی در موفقیت برنامه تجاری شما ایفا میکند.

حالا ما استفاده از Unit Testing را به صورت گام به گام فراخواهیم گرفت.

ابتدا یک پروژه از نوع MVC ایجاد میکنیم و با استفاده از Entity Framework عملیات CRUD را انجام میدهیم.

برنامه ویژوال استدیو خود را باز کنید New->Project :

همانطور که در تصویر بالا مشخص است Create a Unit test Project تیک خورده است.

حال در پنجره Solution Explorer  مشاهده خواهید کرد که یک پروژه هم نام پروژه با پسوند Test. اضافه شده است:

حال روی پوشه Model کلیک راست گزینه Add و سپس Ado.Net Entity Data Model را انتخاب کنید.

مراحل اضافه کردن Model را به صورت تصویری آموزش میدهیم:

 

دراینجا از الگوی Repository استفاده میکنیم پس روی پوشه Model کلیک راست کرده و یک واسط (Interface) جدید اضافه کنید:

سپس متد های زیر را در آن تعریف میکنیم:


    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();  
       }  
    }  

حالا روی پوشه مدل خود کلیک راست کنید و یک کلاس به نام EmployeeRepository اضافه کنید:

سپس از الگوی ساخته شده ارث بری میکنیم و متد های الگوی خود را پیاده سازی میکنیم:


    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);  
            }  
        }  
    }  

سپس روی پوشه کنترلر راست کلیک کنید و یک کنترلر جدید به نام Employee بسازید:

scaffolding کدها و ویو های چهار عمل اصلی را برای شما ایجاد می کند:


    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));  
            }  
      
            //    
            // GET: /Employee/Details/5    
      
            public ViewResult Details(int id)   
            {  
                Employee emp = employeeRepository.GetEmployeeByID(id);  
                return View(emp);  
            }  
      
            //    
            // GET: /Employee/Create    
      
            public ActionResult Create()  
            {  
                return View("Create");  
            }  
      
            //    
            // POST: /Employee/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);  
            }  
      
            //    
            // GET: /Employee/Edit/5    
      
            public ActionResult Edit(int id)   
            {  
                Employee emp = employeeRepository.GetEmployeeByID(id);  
                return View(emp);  
            }  
      
            //    
            // POST: /Employee/Edit/5    
      
            [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);  
            }  
      
            //    
            // GET: /employee/Delete/5    
      
            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);  
            }  
      
            //    
            // POST: /Employee/Delete/5    
      
            [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);  
            }  
      
        }  
    }  

Unit Testing :

نوبت به قسمت unit testing می رسد در پروژه UnitTestingAppInMVC.Tests یک پوشه به نام Model  اضافه میکنیم سپس روی پوشه مدل کلیک راست کرده و یک کلاس جدید به نام InMemoryEmployeeRepository.cs میسازیم و  الگوی Repository که قبلا ساختیم ارث بری میکنیم.

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)   
                {  
                    //Dispose Object Here    
                }  
            }  
            this.disposed = true;  
        }  
  
        public void Dispose()   
        {  
            Dispose(true);  
            GC.SuppressFinalize(this);  
        }  
    }  
} 

سپس یک کنترلر به نام EmployeeControllerTest.cs بسازید در اینجا هم تمام action method ها برای مواردی که میخواهید تست کنید تعریف میکنیم:


    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   
        {  
            /// <summary>    
            /// This method used for index view    
            /// </summary>    
            [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));  
            }  
      
            /// <summary>    
            /// This method used to get employee controller    
            /// </summary>    
            /// <param name="repository"></param>    
            /// <returns></returns>    
            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;  
            }  
      
            /// <summary>    
            /// This method used to get all employye listing    
            /// </summary>    
            [TestMethod]  
            public void GetAllEmployeeFromRepository()   
            {  
                // Arrange    
                Employee employee1 = GetEmployeeName(1, "Rahul Saxena", "rahulsaxena@live.com", "Software Developer", "Noida", "Uttar Pradesh", "India");  
                Employee employee2 = GetEmployeeName(2, "Abhishek Saxena", "abhishek@abhishek.com", "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);  
            }  
      
            /// <summary>    
            /// This method used to get emp name    
            /// </summary>    
            /// <param name="Emp_ID"></param>    
            /// <param name="Name"></param>    
            /// <param name="Email"></param>    
            /// <param name="Designation"></param>    
            /// <param name="City"></param>    
            /// <param name="State"></param>    
            /// <param name="Country"></param>     
            /// <returns></returns>    
            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  
                };  
            }  
      
            /// <summary>    
            /// This test method used to post employee    
            /// </summary>    
      
            [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));  
            }  
      
            /// <summary>    
            ///    
            /// </summary>    
            /// <returns></returns>    
            Employee GetEmployeeID()   
            {  
                return GetEmployeeName(1, "Rahul Saxena", "rahulsaxena@live.com", "Software Developer", "Noida", "Uttar Pradesh", "India");  
            }  
      
            /// <summary>    
            ///    
            /// </summary>    
            [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"]);  
            }  
      
            /// <summary>    
            ///    
            /// </summary>    
            [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 /* roles */ );  
      
            public override IPrincipal User  
            {  
                get   
                {  
                    return _user;  
                }  
                set   
                {  
                    base.User = value;  
                }  
            }  
        }  
    }  

حالا متد های ایجاد شده را تست میکنیم:

نتیجه تست خود را در اینجا ببینید:

 

فایل های ضمیمه