本篇使用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. (四)Jsoup 获取 DOM 元素属性值

    第一节: Jsoup 获取 DOM 元素属性值 Jsoup获取DOM元素属性值 比如我们要获取博客的href属性值: 我们这时候就要用到Jsoup来获取属性的值 : 我们给下示例代码: package ...

  2. (一)问候 HtmlUnit

    第一节: HtmlUnit 简介 htmlunit 是一款开源的java 页面分析工具,读取页面后,可以有效的使用htmlunit分析页面上的内容.项目可以模拟浏览器运行,被誉为java浏览器的开源实 ...

  3. 删除数据库所有存储过程的SQL语句

    --/第1步**********删除所有表的外键约束*************************/ DECLARE c1 cursor for select 'alter table ['+ o ...

  4. KnockoutJs学习笔记(二)

    这篇文章主要用于记录学习Working with observable arrays的测试和体会. Observable主要用于单一个体的修改订阅,当我们在处理一堆个体时,当UI需要重复显示一些样式相 ...

  5. element-ui 2.7.2版本使用 表格展开行 功能遇到的奇葩问题?

    在使用 element-ui 2.7.2版本的时候报下面的错误: [Vue warn]: Error in callback for watcher "data": "E ...

  6. CI框架整合UEditor编辑器上传功能

    最近项目中要使用到富文本编辑器,选用了功能强大的UEditor,接下来就来讲讲UEditor编辑器的上传功能整合. 本文UEditor版本:ueditor1_4_3_utf8_php版本 第一步:部署 ...

  7. CCF CSP 201609-3 炉石传说

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201609-3 炉石传说 问题描述 <炉石传说:魔兽英雄传>(Hearthston ...

  8. bzoj 1820 dp

    最普通dp要4维,因为肯定有一个在上一个的位置,所以可以变为3维,然后滚动数组优化一下. #include<bits/stdc++.h> #define LL long long #def ...

  9. Gitlab Issue Tracker and Wiki(一)

    本节内容: 创建第一个问题 创建第一个合并请求 接受合并请求 工作里程碑 在提交中引用问题 创建维基百科页 使用Gollum管理维基百科 一. 创建问题 1. 登陆Gitlab服务器 2. 切换到想要 ...

  10. React Native网络编程之Fetch

    目录 1.前言 2.什么是Fetch 3.最简单的应用 4.支持的请求参数 - 4.1. 参数详讲 - 4.2. 示例 5.请求错误与异常处理   1. 前言   网络请求是开发APP中不可或缺的一部 ...