本篇使用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. TDictionary 与 TObjectDictionary

    TDictionary 与 TObjectDictionary 的区别是 : TObjectDictionary 可以做到 free的时候 里面的对象 一并free,从而不会出现内存 泄露. 用途: ...

  2. HDU 3068 最长回文(manacher模板题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 题目大意:求字符串s中最长的回文子串 解题思路:manacher模板 代码 #include&l ...

  3. sql中多层循环示例(有游标)

    在需求处理中,我们会遇到需要通过SQL多层循环来处理的问题.如:A表中有8条数据,B表中有10条数据,需要实现A表中的每1条数据对应B表中的10条数据,最后就有了80条数据,从而实现一对多的关系.那如 ...

  4. PHP性能调优,PHP慢日志---善用php-fpm的慢执行日志slow log,分析php性能问题

    众所周知,MySQL有slow query log,根据慢查询日志,我们可以知道那些sql语句有性能问题.作为mysql的好搭档,php也有这样的功能.如果你使用php-fpm来管理php的话,你可以 ...

  5. Ocelot 配置初始

    { "ReRoutes": [ { /*将用户的请求 /post/1 转发到 localhost/api/post/1*/ /* DownstreamPathTemplate:转到 ...

  6. 一步一步学习IdentityServer3 (11) OAuth2

    OAuth中定义了四个Role 资源所有者:这里可以理解为一个用户 资源服务器:如同前面章节中的 Web站点或者WebApi 服务资源站点 客户端:这里是Client,如同Identityserver ...

  7. 007 爬虫(Scrapy库的使用)

    推荐网址: http://scrapy-chs.readthedocs.io/zh_CN/0.24/topics/architecture.html 1.简介 python开发的一个快速,高层次的屏幕 ...

  8. 001 python入门工具准备

    一:准备 1.python官网 https://www.python.org/ 2.使用开发工具Pycharm 3.使用版本 使用2.7.13 4.安装 5.安装pycharm http://www. ...

  9. 洛谷P1527 [国家集训队] 矩阵乘法 [整体二分,二维树状数组]

    题目传送门 矩阵乘法 题目描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入输出格式 输入格式: 第一行两个数N,Q,表示矩阵大小和询问组数: 接下来N行N列一共N* ...

  10. MySQL服务器 IO 100%的案例分析

    [问题] 有台MySQL 5.6.21的数据库实例以写入为主,IO %util接近100% 写入IOPS很高 [分析过程] 1.通过iotop工具可以看到当前IO消耗最高的mysql线程 2.查看线程 ...