本篇使用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的更多相关文章

  1. (转)MVC中的Repository模式

    1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名 ...

  2. MVC中的Repository模式

    1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名 ...

  3. MVC+LINQToSQL的Repository模式之(一)数据工厂 DataContext绑定线程

    namespace Data{    /// <summary>    /// 数据库建立工厂    /// Created By : 张占岭    /// Created Date:20 ...

  4. MVC+LINQToSQL的Repository模式之(二)数据基类

    namespace Data.TEST{    /// <summary>    /// 数据操作基类    /// </summary>    public abstract ...

  5. EntityFramework系列:Repository模式与单元测试

    1.依赖IRepository接口而不是直接使用EntityFramework 使用IRepository不只是架构上解耦的需要,更重要的意义在于Service的单元测试,Repository模式本身 ...

  6. Repository模式介绍汇总

    1.Linq To Sql中Repository模式应用场景 http://www.cnblogs.com/zhijianliutang/archive/2012/02/24/2367305.html ...

  7. MVC架构中的Repository模式 个人理解

    关于MVC架构中的Repository模式   个人理解:Repository是一个独立的层,介于领域层与数据映射层(数据访问层)之间.它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接 ...

  8. 关于MVC EF架构及Repository模式的一点心得

    一直都想写博客,可惜真的太懒了或者对自己的描述水平不太自信,所以...一直都是不想写的状态,关于领域驱动的东西看了不少,但是由于自己水平太差加上工作中实在用不到,所以一直处于搁置状态,最近心血来潮突然 ...

  9. MVC Repository模式

    近来发现很多ASP.NET MVC的例子中都使用了Repository模式,比如Oxite,ScottGu最近发布的免费的ASP.NET MVC教程都使用了该模式.就简单看了下. 在<企业架构模 ...

随机推荐

  1. 20155225 2006-2007-2 《Java程序设计》第四周学习总结

    20155225 2006-2007-2 <Java程序设计>第四周学习总结 教材学习内容总结 对"是一种"语法测试几次之后,总结一句:满足"是一种" ...

  2. 浅析redux

    一 redux 思想 首先,每一个webApp有且只有一个state tree,为方便管理和跟踪state的变化,也为了减少混乱,redux只允许通过发送(dispatch)action的方式来改变s ...

  3. CF 577A 分解因数

    输入一个n 构成一个n*n的表 这个表里的数 第i行j列的值为i*j 问x在这个表里出现了几次 Sample test(s)input10 5output2input6 12output4input5 ...

  4. JavaScript中判断日期是否相等

    问题 做一个节日提示网页,首先获得当前日期,然后与最近的节日比较,如果恰好是同一天,提示"XX节快乐!"否则,提示"离XX节还有X天".判断是否恰好同一天的时候 ...

  5. RabbitMQ系列之高可用集群

    为了实现高可用,我采用LVS+双节点RabbitMq , 架构图如下: 在RabbitMQ之前放了LVS, LVS 采用 rr 轮询算法 , 目的是将请求平均分配到两个真实节点,并配置5672端口监控 ...

  6. Zookeeper服务器集群的搭建与操作

    ZooKeeper 作用:Zookeeper 可以用来保证数据在zk集群之间的数据的事务性一致(原子操作). 介绍:Zookeeper 是 Google 的 Chubby一个开源的实现,是 Hadoo ...

  7. caffe 如何训练自己的数据图片

    申明:此教程加工于caffe 如何训练自己的数据图片 一.准备数据 有条件的同学,可以去imagenet的官网http://www.image-net.org/download-images,下载im ...

  8. 使用 Python 可以做什么?

    翻译自 <Python学习手册(第5版)> Systems Programming Python 对操作系统服务的内置接口使其非常适合编写可移植.可维护的系统管理工具和实用程序 utili ...

  9. iview-cli 项目、iView admin 跨域问题解决方案

    在build 目录的 webpack.dev.config.js 目录中 module.exports = merge(webpackBaseConfig, { devtool: '#source-m ...

  10. HDU Today hdu 2112

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=2112 文章末有一些相应的测试数据供参考. 此题就是一个求最短路的问题,只不过现在的顶点名称变成了字符串而不 ...