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教程都使用了该模式.就简单看了下. 在<企业架构模 ...
随机推荐
- MySQL学习笔记:floor、round —— 取整
在MySQL中做数值处理,需要取整或者四舍五入. floor:函数只返回整数部分,小数部分舍弃: round:函数四舍五入: END 2018-05-29 11:31:22
- SonarQube的安装、配置与使用(windows)
onarQube是管理代码质量一个开放平台,可以快速的定位代码中潜在的或者明显的错误,下面将会介绍一下这个工具的安装.配置以及使用. 准备工作: 1.jdk(不再介绍) 2.sonarqube:htt ...
- 编译原理之正则表达式转NFA
本文转载自http://chriszz.sinaapp.com/?p=257 输入一个正则表达式,输出一个NFA. 我的做法:输入一个字符串表示正则,输出则是把输出到一个.dot文件中并将dot文件编 ...
- Oracle与Sqlserver数据共享
需求:在一个集成平台中有一个主系统使用的是Oralce数据库,子系统使用的SqlServer 数据库,如何让子系统的数据库与主系统的人员同步呢? 思路:通过服务WebService 公开接口 1.与主 ...
- Android Studio 升级为3.1 踩到的坑
原文:https://blog.csdn.net/xiariluoxue/article/details/80050700 AndroidStudio.gradle.buildToolsVersion ...
- Azkaban(三)Azkaban的使用
界面介绍 首页有四个菜单 projects:最重要的部分,创建一个工程,所有flows将在工程中运行. scheduling:显示定时任务 executing:显示当前运行的任务 history:显示 ...
- require demo 记录备份
预览地址 http://127.0.0.1:8020/requireDemo/myNEW/index.html 注意 远程的 非模块的 empty: demo2
- MXNet 中的几个数据集
from mxnet import gluon def transform(data, label): return data.astype('float32') / 255., label.asty ...
- 【原创】MySQL CPU %sys高的案例分析(一)
[现象] 最近关注MySQL CPU告警的问题时,发现有一种场景,有一些服务器最近都较频繁的出现CPU告警,其中的现象是 SYS CPU占比较高. 下面的截图来源于“MySQL CPU报警”采集的文件 ...
- Initramfs 原理和实践
Linux系统启动时使用initramfs (initram file system), initramfs可以在启动早期提供一个用户态环境,借助它可以完成一些内核在启动阶段不易完成的工作.当然ini ...