首页 > 代码库 > MVC单元测试,使用Repository模式、Ninject、Moq

MVC单元测试,使用Repository模式、Ninject、Moq

本篇使用Repository设计MVC项目,使用Ninject作为DI容器,借助Moq进行单元测试。

 

  模型和EF上下文

模型很简单:

public class Foo    {        public int Id { get; set; }        public string Name { get; set; }    }

EF上下文为:

using System.Data.Entity;namespace MvcApplication1.Models{    public class FooBarContext : DbContext    {        public DbSet<Foo> Foos { get; set; }    }}

 

  Repository相关

为了避免在IXXXRepository中有关增删改查等的重复代码,有必要创建一个所有IXXXRepository的基接口:

using System;using System.Linq;using System.Linq.Expressions;namespace MvcApplication1.Repository{    public interface IBaseRepository<T> where T : class    {        IQueryable<T> GetAll();        IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);        void Add(T entity);        void Edit(T entity);        void Delete(T entity);        void Save();    }}

IFooRepository,也可以有自己的接口方法:

using MvcApplication1.Models;namespace MvcApplication1.Repository{    public interface IFooRepository : IBaseRepository<Foo>    {        Foo GetSingle(int fooId);    }}

BaseRepository是一个抽象类,提供了所有XXXRepository的泛型基类实现,并实现 IBaseRepository接口:

using System.Data.Entity;using System.Linq;namespace MvcApplication1.Repository{    public abstract class BaseRepository<C,T> : IBaseRepository<T> where T : class where C : DbContext,new()    {        private C _db = new C();        public C Db        {            get { return _db; }            set { _db = value; }        }        public System.Linq.IQueryable<T> GetAll()        {            IQueryable<T> query = _db.Set<T>();            return query;        }        public System.Linq.IQueryable<T> FindBy(System.Linq.Expressions.Expression<System.Func<T, bool>> predicate)        {            IQueryable<T> query = _db.Set<T>().Where(predicate);            return query;        }        public void Add(T entity)        {            _db.Set<T>().Add(entity);        }        public void Edit(T entity)        {            _db.Entry(entity).State = EntityState.Modified;        }        public void Delete(T entity)        {            _db.Set<T>().Remove(entity);        }        public void Save()        {            _db.SaveChanges();        }    }}

FooRepository不仅派生于BaseRepository<FooBarContext, Foo>,还需要实现IFooRepository约定的接口方法:

using System.Linq;using MvcApplication1.Models;namespace MvcApplication1.Repository{    public class FooRepository : BaseRepository<FooBarContext, Foo>,IFooRepository    {        public Foo GetSingle(int fooId)        {            var query = GetAll().FirstOrDefault(x => x.Id == fooId);            return query;        }    }}

 

  Ninject控制器工厂

通过GuGet安装Ninjct,创建Ninject控制器工厂:

using System.Web.Mvc;using MvcApplication1.Repository;using Ninject;namespace MvcApplication1.Extension{    public class NinjectControllerFactory : DefaultControllerFactory    {        private IKernel ninjectKernel;        public NinjectControllerFactory()        {            ninjectKernel = new StandardKernel();        }        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, System.Type controllerType)        {            return controllerType == null ? null : (IController) ninjectKernel.Get(controllerType);        }        private void AddBindings()        {            ninjectKernel.Bind<IFooRepository>().To<FooRepository>();            ninjectKernel.Bind<IBarRepository>().To<BarRepository>();        }    }}

并在全局注册:

protected void Application_Start()        {            ......            ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());        }

 

  创建FooController,包含增删改查

using System;using System.Web.Mvc;using MvcApplication1.Models;using MvcApplication1.Repository;namespace MvcApplication1.Controllers{    public class FooController : Controller    {        private readonly IFooRepository _fooRepository;        public FooController(IFooRepository fooRepository)        {            _fooRepository = fooRepository;        }        public ViewResult Index()        {            var model = _fooRepository.GetAll();            return View(model);        }        public ActionResult Details(int id)        {            var model = _fooRepository.GetSingle(id);            if (model == null)            {                return HttpNotFound();            }            return View(model);        }        public ActionResult Edit(int id)        {            var model = _fooRepository.GetSingle(id);            if (model == null)            {                return HttpNotFound();            }            return View(model);        }        [ActionName("Edit"), HttpPost]        public ActionResult Eidt_Post(Foo foo)        {            if (ModelState.IsValid)            {                try                {                    _fooRepository.Edit(foo);                    _fooRepository.Save();                    return RedirectToAction("Details", new { id = foo.Id });                }                catch (Exception ex)                {                   ModelState.AddModelError(string.Empty, "出错了:" + ex.Message);                }            }            return View(foo);        }        public ActionResult Create()        {            return View();        }        [ActionName("Create"), HttpPost]        public ActionResult Create_Post(Foo foo)        {            if (ModelState.IsValid)            {                try                {                    _fooRepository.Add(foo);                    _fooRepository.Save();                    return RedirectToAction("Details", new {id = foo.Id});                }                catch (Exception ex)                {                   ModelState.AddModelError(string.Empty, "出错了:"+ex.Message);                }            }            return View(foo);        }        public ActionResult Delete(int id)        {            var model = _fooRepository.GetSingle(id);            if (model == null)            {                return HttpNotFound();            }            return View(model);        }        [ActionName("Delete"),HttpPost]        public ActionResult Delete_Post(int id)        {            var model = _fooRepository.GetSingle(id);            if (model == null)            {                return HttpNotFound();            }            _fooRepository.Delete(model);            _fooRepository.Save();            return RedirectToAction("Index");        }    }}

 

  单元测试

通过NuGet安装Moq,借助Moq来模拟接口方法的返回值。

→初始化

private IFooRepository fooRepository;        [TestInitialize]        public void Initialize()        {            Mock<IFooRepository> mock = new Mock<IFooRepository>();            mock.Setup(m => m.GetAll()).Returns(new Foo[]            {                new Foo(){Id = 1, Name = "Fake Foo 1"},                 new Foo(){Id = 2, Name = "Fake Foo 2"},                 new Foo(){Id = 3, Name = "Fake Foo 3"},                 new Foo(){Id = 4, Name = "Fake Foo 4"}            }.AsQueryable());            mock.Setup(m =>            m.GetSingle(It.Is<int>(i =>i == 1 || i == 2 || i == 3 || i == 4))).Returns<int>(r => new Foo            {                Id = r,                Name = string.Format("Fake Foo {0}", r)            });            fooRepository = mock.Object;        }

 

→测试返回类型

[TestMethod]        public void is_index_return_model_type_of_iqueryable_foo()        {            //Arragne            FooController fooController = new FooController(fooRepository);            //Act            var indexModel = fooController.Index().Model;            //Assert            Assert.IsInstanceOfType(indexModel, typeof(IQueryable<Foo>));        }        [TestMethod]        public void is_details_returns_type_of_ViewResult()        {            //Arrange            FooController fooController = new FooController(fooRepository);            //Act            var detailsResult = fooController.Details(1);            //Assert            Assert.IsInstanceOfType(detailsResult, typeof(ViewResult));        }        [TestMethod]        public void is_details_returns_type_of_HttpNotFoundResult()        {            //Arrange            FooController fooController = new FooController(fooRepository);            //Act            var detailsResult = fooController.Details(5);            //Assert            Assert.IsInstanceOfType(detailsResult, typeof(HttpNotFoundResult));        }

→测试返回集合类型Model的数量  

2

 

结果:

1

 

参考资料:               
How to Work With Generic Repositories on ASP.NET MVC and Unit Testing Them By Mocking