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的更多相关文章
- (转)MVC中的Repository模式
1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名 ...
- MVC中的Repository模式
1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名 ...
- MVC+LINQToSQL的Repository模式之(一)数据工厂 DataContext绑定线程
namespace Data{ /// <summary> /// 数据库建立工厂 /// Created By : 张占岭 /// Created Date:20 ...
- MVC+LINQToSQL的Repository模式之(二)数据基类
namespace Data.TEST{ /// <summary> /// 数据操作基类 /// </summary> public abstract ...
- EntityFramework系列:Repository模式与单元测试
1.依赖IRepository接口而不是直接使用EntityFramework 使用IRepository不只是架构上解耦的需要,更重要的意义在于Service的单元测试,Repository模式本身 ...
- Repository模式介绍汇总
1.Linq To Sql中Repository模式应用场景 http://www.cnblogs.com/zhijianliutang/archive/2012/02/24/2367305.html ...
- MVC架构中的Repository模式 个人理解
关于MVC架构中的Repository模式 个人理解:Repository是一个独立的层,介于领域层与数据映射层(数据访问层)之间.它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接 ...
- 关于MVC EF架构及Repository模式的一点心得
一直都想写博客,可惜真的太懒了或者对自己的描述水平不太自信,所以...一直都是不想写的状态,关于领域驱动的东西看了不少,但是由于自己水平太差加上工作中实在用不到,所以一直处于搁置状态,最近心血来潮突然 ...
- MVC Repository模式
近来发现很多ASP.NET MVC的例子中都使用了Repository模式,比如Oxite,ScottGu最近发布的免费的ASP.NET MVC教程都使用了该模式.就简单看了下. 在<企业架构模 ...
随机推荐
- (四)Jsoup 获取 DOM 元素属性值
第一节: Jsoup 获取 DOM 元素属性值 Jsoup获取DOM元素属性值 比如我们要获取博客的href属性值: 我们这时候就要用到Jsoup来获取属性的值 : 我们给下示例代码: package ...
- (一)问候 HtmlUnit
第一节: HtmlUnit 简介 htmlunit 是一款开源的java 页面分析工具,读取页面后,可以有效的使用htmlunit分析页面上的内容.项目可以模拟浏览器运行,被誉为java浏览器的开源实 ...
- 删除数据库所有存储过程的SQL语句
--/第1步**********删除所有表的外键约束*************************/ DECLARE c1 cursor for select 'alter table ['+ o ...
- KnockoutJs学习笔记(二)
这篇文章主要用于记录学习Working with observable arrays的测试和体会. Observable主要用于单一个体的修改订阅,当我们在处理一堆个体时,当UI需要重复显示一些样式相 ...
- element-ui 2.7.2版本使用 表格展开行 功能遇到的奇葩问题?
在使用 element-ui 2.7.2版本的时候报下面的错误: [Vue warn]: Error in callback for watcher "data": "E ...
- CI框架整合UEditor编辑器上传功能
最近项目中要使用到富文本编辑器,选用了功能强大的UEditor,接下来就来讲讲UEditor编辑器的上传功能整合. 本文UEditor版本:ueditor1_4_3_utf8_php版本 第一步:部署 ...
- CCF CSP 201609-3 炉石传说
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201609-3 炉石传说 问题描述 <炉石传说:魔兽英雄传>(Hearthston ...
- bzoj 1820 dp
最普通dp要4维,因为肯定有一个在上一个的位置,所以可以变为3维,然后滚动数组优化一下. #include<bits/stdc++.h> #define LL long long #def ...
- Gitlab Issue Tracker and Wiki(一)
本节内容: 创建第一个问题 创建第一个合并请求 接受合并请求 工作里程碑 在提交中引用问题 创建维基百科页 使用Gollum管理维基百科 一. 创建问题 1. 登陆Gitlab服务器 2. 切换到想要 ...
- React Native网络编程之Fetch
目录 1.前言 2.什么是Fetch 3.最简单的应用 4.支持的请求参数 - 4.1. 参数详讲 - 4.2. 示例 5.请求错误与异常处理 1. 前言 网络请求是开发APP中不可或缺的一部 ...