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

你可能感兴趣的:(repository)