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的数量

结果:

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

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

时间: 2024-10-11 14:06:52

MVC单元测试,使用Repository模式、Ninject、Moq的相关文章

MVC中的Repository模式

1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名为Repository来存放业务逻辑. 3.继续添加一个类库项目,命名为MyRepository.Domain,添加两个文件夹Models和Infrastructure. Models来存放实体,Infrastructure来存放几个基本的类.现在目录结构已经搭建好了. 4.用NuGet确保三个项目

关于Repository模式

定义(来自Martin Fowler的<企业应用架构模式>): Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects. 个人理解:Repository是一个独立的层,介于领域层与数据映射层(数据访问层)之间.它的存在让领域层感觉不到数据访问层的存在,它提供一 个类似集合的接口提供给领域层进行领域对象的访问.Reposit

Repository模式

近来发现很多ASP.NET MVC的例子中都使用了Repository模式,比如Oxite,ScottGu最近发布的免费的ASP.NET MVC教程都使用了该模式.就简单看了下. 在<企业架构模式>中,译者将Repository翻译为资源库.给出如下说明:通过用来访问领域对象的一个类似集合的接口,在领域与数据映射层之间进行协调. 在<领域驱动设计:软件核心复杂性应对之道>中,译者将Repository翻译为仓储,给出如下说明:一种用来封装存储,读取和查找行为的机制,它模拟了一个对象

LCLFramework框架之Repository模式

Respository模式在示例中的实际目的小结一下 Repository模式是架构模式,在设计架构时,才有参考价值: Repository模式主要是封装数据查询和存储逻辑: Repository模式实际用途:更换.升级ORM 引擎,不影响业务逻辑: Repository模式能提高测试效率,单元测试时,用Mock对象代替实际的数据库存取,可以成倍地提高测试用例运行速度. Repository与Dal的区别 Repository是DDD中的概念,强调Repository是受Domain驱动的,Re

Repository模式与UnitOfWorks模式的运用

软件开发就像是一个江湖,而设计模式就是一本高深的秘籍每读一次.用一次.想一次都能得到新的领悟,让我们的设计技能有所提高.开始时我们可能会“为了模式而模式”,让代码变得乱78糟甚至难以让人理解,但随着设计能力的提高与模式的运用和理解,慢慢地我们可能会忘掉模式随心所欲,此时再读读代码或者你已经发现自己的工程已融合模式之美—"模式为设计而生,设计为需求而活".在开篇突然想分享一下这10几年用模式的一点小小的领悟. IRepository 与 IUnitOfWork 在2011年我曾在Code

Asp.net Webform 使用Repository模式实现CRUD操作代码生成工具

Asp.net Webform 使用Repository模式实现CRUD操作代码生成工具 介绍 该工具是通过一个github上的开源项目修改的原始作者https://github.com/Superexpert/WebFormsScaffolding 我在他的基础上添加了一对多实体类的新增,修改,删除操作,并实现了Repository设计模式,修改了一些页面布局,添加了一些DynamicData动态字段,我的开源项目地址https://github.com/neozhu/WebFormsScaf

基于Repository模式设计项目架构—你可以参考的项目架构设计

关于Repository模式,直接百度查就可以了,其来源是<企业应用架构模式>.我们新建一个Infrastructure文件夹,这里就是基础设施部分,EF Core的上下文类以及Repository层都放在这里面.新建一个IReposotory的接口,其内容就是封装了基本的CRUD: public interface IRepository<TEntity> where TEntity : class { ///获取当前实体的查询数据集 IQueryable<TEntity&

Entity Framework Repository模式

Repository模式之前 如果我们用最原始的EF进行设计对每个实体类的“C(增加).R(读取).U(修改).D(删除)”这四个操作. 第一个:先来看看查询,对于实体类简单的查询操作,每次都是这样的过程会在代码中拥有大量的重复 极为类似的代码段. using (var db = new EFContext("EFContext")) { var persons = db.Persons.Where(t => t.PersonName == "aehyok")

基于ASP.NET WPF技术及MVP模式实战太平人寿客户管理项目开发(Repository模式)

亲爱的网友,我这里有套课程想和大家分享,如果对这个课程有兴趣的,可以加我的QQ2059055336和我联系. 课程背景 本课程是教授使用WPF.ADO.NET.MVVM技术来实现太平人寿保险有限公司保险客户管理系统,是学习WPF开发中的一门主打课程之一. WPF是一个框架,它供程序员开发出媲美Mac程序的酷炫界面. Blend是一种工具,可以在美工板上绘制形状.路径和控件,然后修改其外观和行为,从而直观地设计应用程序 Repository\MVVM\MVP设计模式是WPF常用的系统架构 Auto