首先要啰嗦几句。

单元测试是TDD的重要实践方法,也是代码质量的一种保证手段。在项目的工程化开发中,研发人员应该尽量保证书写Unit Test,即使不使用TDD。

(VS中,我们可以直接使用微软提供的一套单元测试框架,一般使用足够了,特别需求的话,可以使用其他更好的框架。)

书写单元测试时,我们并不一定真的要去连接数据库,毕竟就算只使用自己计算机上的研发数据库,也不能保证数据正确性和完备性,毕竟自己经常会操作些垃圾数据。

这个时候就需要模拟一个“数据库”来构造我们想要的一些数据。这个就是Mock最直接的需求。然后当我们进一步实践,会发现我们的Service层等,也可以用Mock模拟对象,而不是非要去new一个真实对象。真实场景是当我们研发小组合作时,你作为更高层的研发人员,可能只拿到了服务层的接口,具体的实现类你的同事还在研发中,这个时候你要做Unit Test,就只有模拟一个假的Service实现了。

C#的单元测试框架中,有一套Mock框架就叫Moq。

Moq可以直接在VS 2013及以后的版本中通过Nuget获取。以前的版本的VS可以到github上下载Moq的dll。

Moq的github地址为:Moq

Moq的QuickStart页面为:QuickStart 深入学习,可以直接看此文档。

MVC中,最直接需要模拟的应该就是HttpContext相关对象,如HttpRequest、Server、Session等对象。以HttpRequest为例。

首先,我们要知道, controller中相关HttpContext的对象是ControllerContext,它就是HttpContextBase。模拟的HttpContext通过它绑定给Controller。

controller.ControllerContext = new ControllerContext(mockContext.Object, new RouteData(), controller);

mockContext.Object就是我们用Moq模拟的HttpContextBase。

这里是绑定的代码,倒推回去,我们应该先生成mockContext,如下:

private Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>();

实际我们代码可能使用的是Request["xxx"]、Session["yyy"],这些对象又依赖于HttpContextBase,所以我们需要模拟它们,然后绑定到 mockContext。如下

 var mockRequest = new Mock<HttpRequestBase>();
//模拟Request["xxx"]
if (dataIndexed != null)
{
foreach (var pair in dataIndexed)
{
mockRequest.Setup(x => x[pair.Key]).Returns(pair.Value);
}
}
mockContext.SetupGet(x => x.Request).Returns(mockRequest.Object);

 注意最后一句就是将模拟的Request绑定到模拟的HttpContextBase上。代码含义是:当通过mockContext.Request(即它的get方法)得到Request对象时,把mockRequest模拟的HttpRequest对象返回。 

Moq的方法都是比较直白的含义,如上就是:SetupGet(x => x.Request).Returns($$$),针对对象Request使用Get方法时,Returns相应的对象$$$。其他的对象,不管是HttpContextBase的还是Request的再子层对象,都通过这样的方法设置,前提是相应的类中有此属性(get,set)。

回过头继续看上面代码这段mockRequest.Setup(x => x[pair.Key]).Returns(pair.Value); 这个就是直接针对值进行设置,这里是针对类的Indexedr进行设置。

还有一些其他方法,需要时就看QuickStart了。

另外,针对全局的HttpContext对象,在单元测试中,它是null的,所以为了保证单元测试可进行,需要对其进行包装,在项目中使用包装的类进行访问。这样,单元测试时,就注入自己模拟的HttpContext对象。然而HttpContext是sealed类,是不能被Mock的。所以我们可以在包装类中,使用两个对象,分别指向Mock的对象和真实的HttpContext,依据是否模拟的判断在代码中选择调用。也可以使用HttpContextWrapper来包装HttpContext,因为HttpContextWrapper是HttpContextBase的实现。如:

 /// <summary>
/// 全局HttpContext的包装类,以便单元测试
/// </summary>
public class CmsHttpContext
{
/// <summary>
/// 当前单例对象
/// </summary>
private static CmsHttpContext _instance = new CmsHttpContext();
/// <summary>
/// 包装的HttpContext
/// </summary>
private static HttpContextBase wrapper = null;
/// <summary>
/// 是否被包装
/// </summary>
private static bool IsWrap = false; private CmsHttpContext()
{ }
/// <summary>
/// 当前HttpContext对象
/// </summary>
public static HttpContextBase Current
{
get
{
if (!IsWrap)
{
wrapper = new HttpContextWrapper(HttpContext.Current);
}
return wrapper;
}
} /// <summary>
/// 包装外部 HttpContext,仅用于单元测试中
/// </summary>
/// <param name="context"></param>
public static void Wrap(HttpContextBase context)
{
IsWrap = true;
wrapper = context;
}
}

 其他要注意的点:

1.Action的方法直接调用即可以执行。针对ViewBag.XXX,使用Controller对象调用,如mController.ViewBag.XXX

2.JsonResult中Json(XXX),如果XXX是动态类型的话,它在传输后会变成object,单元测试中无法识别它相应的属性,可以使用框架ExposedObject(Nuget中可以直接下载)进行包装,将其包装回dynamic进行测试,如下:

var jsonData = Exposed.From(result.Data);

            Assert.IsTrue(jsonData.total > 0);
Assert.IsTrue(jsonData.list.Count > 0);

  

Asp.net MVC 单元测试 简要笔记的更多相关文章

  1. 如鹏网学习笔记(十五)ASP.NET MVC核心基础笔记

    一.ASP.Net MVC简介 1,什么是ASP.NET MVC? HttpHandler是ASP.net的底层机制,如果直接使用HttpHandler进行开发难度比较大.工作量大.因此提供了ASP. ...

  2. ASP.NET MVC Routing学习笔记(一)

    Routing在ASP.NET MVC中是非常核心的技术,属于ASP.NET MVC几大核心技术之一,在使用Routing之前,得先引入System.Web.Routing,但其实不用这么麻烦,因为在 ...

  3. 精通 ASP.NET MVC 4 学习笔记(一)

    这里记录着从 P132 到 P192 的内容.水分很足,大部分是书上的代码,我只加了一些基于我自己的理解的能帮助初学者看懂的注释,并且把书中的部分内容做了一些的拓展. 建立数据层 设置 DI 容器 / ...

  4. 《asp.net mvc实战》笔记

    对于大部分复杂的项目来说,可能不会在Models文件夹中放置你的模型.一般来说,最好的方法是将你的领域模型放在独立的项目中.这样其他应用程序可以在使用该项目而不必依赖于你的MVC应用程序.我们建议你只 ...

  5. ASP.Net MVC OA项目笔记<二>

    1.1.0 创建数据层 1.1.1 CZBK.ItcastOA.IDAL 引用 CZBK.ItcastOA.Model 1.2.1 给IDAL添加一个接口IUserInfoDal 里面写增删改查分页的 ...

  6. ASP.NET MVC过滤器学习笔记

    1.过滤器的两个特征 1.他是一种特性,可以引用到控制器类和Action方法上.比如下图 这里控制器类和action方法都引用了过滤器,这个过滤器是用来做授权的 2.特征继承自FilterAttrib ...

  7. ASP.Net MVC OA项目笔记<六>

    1.1.1 开始写业务,先写业务的实现再写业务的接口 业务类中也是有写增删改查公用的方法 引用Model,IDAL,DALFactory BLL添加两个类 UserInfoService,BaseSe ...

  8. ASP.Net MVC OA项目笔记<五>

    1.1.1  抽象工厂封装数据操作类实例创建,然后DBSession调用抽象工厂,修改DBSession CZBK.ItcastOA.DALFactory数据会话层调数据层不能直接new,要封装一下解 ...

  9. ASP.Net MVC OA项目笔记<四>

    1.1.1 EF线程唯一 在数据层中用到了EF的实例,在数据会话层也用到了,所以在一个请求中只能创建一个EF实例(线程内唯一对象),把它封装成工厂类 1.1.2 为了防止相互引用,循环引用,所以这个工 ...

随机推荐

  1. 基于Azure blob storage T级别HBase表恢复

    为减少Hbase集群压力,提高性能,我们将HBase库中的数据移到另外的存储,下面记录当我需要对数据进行计算时,数据恢复的过程 目录: Azure storage explorer 工具 数据复制 元 ...

  2. Android 动画的分类

    分为三类: View Animation (补间动画 Tween动画) Drawable Animation(帧动画 Frame动画) Property Animation(android 3.0引入 ...

  3. python 函数简介之三

    1. 非固定个数的实参----参数组,* args ,其结果将以元组的形式呈现出来 def func(*args): print(args) func(1,2,3,4,5) #第一种调用方式, 多个实 ...

  4. C#Redis列表List

    一.前戏 在Redis中,List类型是按照插入顺序排序的字符串链表.和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素.在插入时,如果该键并不存在,Redis将 ...

  5. 将int,bigint整型数值可逆转换字符串

    将 Int 和 BigInt 类型数值转换为字符串的可逆方法,可用于缩短网址或记录的ID转换等. 如: 9223372036854775807 => aZl8N0y58M7 class Conv ...

  6. CentOS Gnome 识别 NTFS-3G

    安装完NTFS-3G后,使用命令行已经可以正常挂载NTFS分区了 但如果是源码编译安装(epel yum 库也有),还需要添加一个软链接,才能点击Gnome的左边栏进行挂载,因为Gnome挂载NTFS ...

  7. windows 下安装Python

    一.下载 官网地址:https://www.python.org/,64位的版本 二.安装 32位和64位的版本安装起来没有区别,双击打开后,第一步要记得勾上add python to Path 选项 ...

  8. oracle建表的时候同时创建主键,外键,注释,约束,索引

    --主键create table emp (id number constraint id_pr primary key ,name1 varchar(8));create table emp9 (i ...

  9. Ubuntu 16.04 samba相关配置

    samba是 SMB/CIFS网络协议的重新实现,它作为NFS的补充使得在Linux和Windows系统之间进行文件共享.打印更容易实现. 相关介绍: SAMBA套件: (1)samba:这个套件主要 ...

  10. log4j基本使用方法

    通过配置文件可知,我们需要配置3个方面的内容: 1.根目录(级别和目的地) 2.目的地(控制台和文件等) 3.输出样式 Log4j由三个重要的组件构成: 1.日志信息的优先级 日志信息的优先级从高到低 ...