转使用Moq让单元测试变得更简单

【ASP.Net MVC3 】使用Moq让单元测试变得更简单

前几天调查完了unity。现在给我的任务是让我调查Moq。

以下是自己找了资料,总结并实践的内容。如果有表述和理解错误的地方。恳请指正。

什么是Moq?

 

Moq(英语发音是Mock-you 或者只是mock)是一个针对.Net开发的模拟库,它从开始就完全充分利用了.NET3.5(LINQ表达式树)和C#3.0的新特性(lambda表达式)。它的目标是让模拟以一种自然的方式与现有单元测试进行集成,使它更加简单、直观,以避免开发人员被迫重写测试或高成本的学习测试框架。这使它成为了一个高生产力、类型安全、重构友好的模拟库。

从哪得到Moq?

 

如果你看过我的其他文章,我们可以直接使用 VS中的插件Nuget来获取Moq并且引用到指定的项目。

否则,我们可以从http://code.google.com/p/moq/这里得到Moq的最新版本。

可以模拟什么?局限性

 

首先,模拟的类不能是密封的。

其次,你不能直接模拟静态方法。因为Moq只能创建模拟对象实例。在这种情况下,间接的解决方案是我们可以在要模拟对象外包装一层,并且去模拟这个新对象。这种模式被称为适配器模式。

通常我们测试一个方法,它有可能调用好几个service。但是每次都去访问这些service的代价是很高的。我们可以通过模拟的方法让它模拟访问service,并且根据不同请求模拟返回响应的结果。

Moq原理

 

Moq是如何办到的?它只需要一个接口类型就可以生产一个对象?没错,就是这样。Moq使用 Castle DynamicProxy 完成这个任务。基本原理就是它利用反射机制的 Emit 功能动态生成一个空类型(也就是所有接口的方法都实例化,但是没有任何功能,只是一个程序骨架)。所以Mock的能力就在于可以利用DynamicProxy的机制快速生产出一个假对象来,用于模仿真对象的行为。

 

Moq中的重要成员

 

Mock

通过这个类,我们可以得到一个Mock<T>对象。T可以是接口,也可以是类。它有一个public 和virtual属性。让我们看看下边的例子:

        //define interface to be mocked

public interface IFake
{

bool DoSomething(string actionname);

}

//define the test method

[TestMethod]

public void Test_Interface_IFake()
{

//make a mock Object by Moq

var mo = new Mock<IFake>();

//Setup our mock object

mo.Setup(foo => foo.DoSomething("Ping"))

.Returns(true);

//Assert it!

Assert.AreEqual(true, mo.Object.DoSomething("Ping"));
}
复制代码

在上边的代码,我们通过传递泛型参数IFake去创建Mock<IFake>的实例 模拟接口IFake。

接下来我们要调用Setup()方法去创建我们的模拟对象。注意,Setup方法的参数是一个lambda表达式。我们可以这样理解:当被模拟的对象foo调用它自己的方法DoSomething(),并且参数是Ping。添加后缀 Return (true)我们可以理解为:前边的请求返回结果为真。这是我们指定的返回值。当一个请求调用DoSomething()方法时。如果传入的参数是Ping,那么我们会返回true。接下来,我们添加一个断言,去判断是否能得到预期结果。

注:Foo仅仅是一个词用作通用替代真实的东西,特别是在讨论技术想法和问题.

It

 

这是一个静态类,定义了静态的泛型方法:Is<TValue>, IsAny<TValue>, IsInRange<TValue>, 和IsRegex。去过滤参数。看看下边例子:

public interface IEmailSender
{

bool Send(string subject, string body, string email);

}
[TestMethod]

public void User_Can_Send_Password()
{

var emailMock = new Mock<IEmailSender>();

emailMock

.Setup(sender => sender.Send(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))

.Returns(true);

}
复制代码

任何时候调用Send()方法,只要传入的参数是任何的string,我们定义他会返回true。

我们也可以根据lambda的优势订制一个规则:

          

  var productRepository = new Mock<IProductRepository>();

productRepository

.Expect(p => p.Get(It.Is<int>(id => id > 0 && id < 6)))

.Returns(newProduct.Object);
复制代码

 

这样我们可以设置这个id在0和6之间的时候才会返回一个新的对象。上边提及到的其他方法,我们可以参考这里的教程 Moq’s QuickStart

此外,这个类的增强版是 Match<T>,你完全可以自定义模拟规则。

MockBehavior

 

这个类用于模拟对象的行为。就像是否按照默认的模式去模拟。让我们进一步看看他的定义:

namespace Moq
{
// Summary:
// Options to customize the behavior of the mock.
public enum MockBehavior
{
// Summary:
// Causes the mock to always throw an exception for invocations that don't have
// a corresponding setup.
Strict = 0,
//
// Summary:
// Will never throw exceptions, returning default values when necessary (null
// for reference types, zero for value types or empty enumerables and arrays).
Loose = 1,
//
// Summary:
// Default mock behavior, which equals Moq.MockBehavior.Loose.
Default = 1,
}
}
复制代码

 

现在,看看如下例子:

 

var mock = new Mock<IFake>(MockBehavior.Strict);

指定了mock行为是精准的,如果没有按照预期的Setup就会抛出异常。

MockFactory

 

这是一个模拟对象的工厂,我们不仅仅可以定制创建模拟对象的配置,也可以成批测试它们。看看下边例子:

 

var factory = new MockFactory(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock };

// Create a mock using the factory settings

var fooMock = factory.Create<IFake>();

// Create a mock overriding the factory settings

var barMock = factory.Create<IEmailSender>(MockBehavior.Loose);

// Verify all verifiable expectations on all mocks created through the factory

factory.Verify();
复制代码

在前边,我们已经介绍了传统方式的创建模拟对象,它不会去真正的调用方法,而是仅仅去执行一些假设:如果...那么返回... 。

在下边的例子里,我将继续介绍一些Mock<T> 类中基本并且重要的方法。

Verification

 

有时候,我们要确定一个方法是否被调用了,或者甚至要知道它被调用了多少次。一个比较传统的方式是使用Verify()方法。看看下边例子:

mock.Verify(foo => foo.DoSomething("Ping"), Times.Once());

上边的代码尝试验证DoSomething("Ping")需要被调用,并且只调用一次。出了Once选项,这里也有更多的选项可供你选择去决定这个方法需要被调用多少次。如: AtLeast, AtLeastOnce, AtMost, AtMostOnce, Between, Equals, Exactly, Never, 和Once

一旦我们已经模拟了对象,验证将是个轻松的任务,看看下边的例子:

   [TestMethod]

public void Test_FindByName_GetCalled()
{

// create some mock data

IList<Product> products = new List<Product>

{

new Product { ProductId = 1, Name = "C# Unleashed",

Description = "Short description here", Price = 49.99 },

new Product { ProductId = 2, Name = "ASP.Net Unleashed",

Description = "Short description here", Price = 59.99 },

new Product { ProductId = 3, Name = "Silverlight Unleashed",

Description = "Short description here", Price = 29.99 }

};

Mock<IProductRepository> mock = new Mock<IProductRepository>();

//mock

//.Setup(sender => sender.FindById(It.IsAny<int>()))

//.Returns((int s) => products.Where(

// x => x.ProductId == s).Single());

mock.Object.FindById(1);

mock

.Verify(x => x.FindById(1), Times.Once());

}
}
复制代码

在上边的例子里,有两个地方值得注意。

首先是mock.Object.FindById(1)。为了在这个case里让一切变得简单,我们直接调用mock.Object的方法。为什么呢?因为我们这个case只关注这个方法被调用的次数,而不关注返回值。当然,在实际的应用中我们很少这样做。

第二,你有没有注意到被注释掉的句子。它仅仅是一个“如果,那么”句子。意思是说,如果Setup 好了,就返回我们定义的值。

由于调查的时间有限,还有很多关于Mock给力的地方我没有涉及到。我们可以去查看官方的资料。欢迎共同讨论。

 

 

参考资料

http://stephenwalther.com/blog/archive/2008/06/12/tdd-introduction-to-moq.aspx

http://code.google.com/p/moq/wiki/QuickStart
http://codeclimber.net.nz/archive/2009/10/23/10-resources-to-learn-moq.aspx
http://blogs.clariusconsulting.net/kzu/tag/moq/

http://blog.miniasp.com/post/2010/09/16/ASPNET-MVC-Unit-Testing-Part-03-Using-Mock-moq.aspx
http://dotnetslackers.com/articles/aspnet/Built-in-Unit-Test-for-ASP-NET-MVC-3-in-Visual-Studio-2010-Part-2.aspx#s4-using-moq-framework

转载于:https://www.cnblogs.com/baiyu/archive/2012/01/11/2319588.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web Service实现分布式服务的基本原理

简单的说&#xff0c; 就是客户端根据WSDL 生成 SOAP 的请求消息&#xff0c; 通过 HTTP 传输方式&#xff08;也可以是其它传输方式&#xff0c; 如 FTP 或STMP 等&#xff0c;目前 HTTP 传输方式已经成为 J2EE Web Service 的标准&#xff09;传给对方&#xff0c; 服务方实现…

使用docker部署mysql主从复制集群

一、环境搭建 虚拟机环境&#xff1a;centos7 IP:192.168.37.134 用户名&#xff1a;root 密码&#xff1a;123 启动3个容器&#xff0c;一个是master&#xff0c;端口是3307&#xff0c;另外两个是slaver&#xff0c;端口是3308和3309 docker pull mysql:5.7docker run -p …

特殊的质数肋骨

特殊的质数肋骨 时间限制: 0 Sec 内存限制: 128 MB题目描述 农民约翰的母牛总是生产出最好的肋骨。你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们。 农民约翰确定他卖给买方的是真正的质数肋骨,是因为从右边开始切下肋骨,每次还剩下的肋骨上的数字都组成一个质…

学滑冰

为什么80%的码农都做不了架构师&#xff1f;>>> http://www.cnr.cn/wcm/zhuanti/harb/bxyd/t20041230_163420.html 学滑冰&#xff08;一&#xff09; 到冰场上穿冰刀奔驰豪情一番&#xff0c;真是今人激情万千&#xff0c;你会滑冰吗&#xff1f;如果你还不会滑冰…

Java-Redis 热部署问题

项目请求时报错&#xff1a;java.lang.ClassCastException: cn.xingaohbd.seckil.model.User cannot be cast to cn.xingaohbd.seckil.model.User at cn.xingaohbd.seckil.service.impl.UserServiceImpl.getUser(UserServiceImpl.java:33) ~[classes/:na] at cn.xingaohbd.seck…

vs2013编译boost1.55.0 32/64位

在使用vs2013编译boost-1.55.0之前&#xff0c;先要给boost做下修改&#xff1a; boost_1_55_0\boost\intrusive\detail\has_member_function_callable_with.hpp line:222 template<class U> static BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION…

ICE简单介绍及使用示例

1、ICE是什么&#xff1f; ICE是ZEROC的开源通信协议产品&#xff0c;它的全称是&#xff1a;The Internet Communications Engine&#xff0c;翻译为中文是互联网通信引擎&#xff0c;是一个面向对象的中间件&#xff0c;使我们能够以最小的代价构建分布式应用程序。ICE使我们…

AtCoder Grand Contest 017

AtCoder Grand Contest 017 A - Biscuits 有\(n\)个数&#xff0c;问有多少个集合的数的和模\(2\)余\(P\)。 随便\(dp\)一下就好了。 #include<iostream> #include<cstdio> using namespace std; #define ll long long inline int read() {int x0;bool tfalse;char…

CentOS 6.2 安装教程

一、CentOS简介 CentOS是Linux的发行版之一&#xff0c;它安全、稳定、高效&#xff0c;是我最喜欢的Linux发行版之一。CentOS根据Red Hat Enterprise Linux开放源代码编译而成&#xff0c;与RedHat Linux并没有什么本质上的差别。但Red Hat Enterprise Linux是商业软件&#x…

浅入ICE组件编程

转载网页&#xff1a;http://blog.csdn.net/liuxuezong/article/details/26745041 一、ICE介绍 ICE是ZeroC公司开发的一款高效的开源中间件平台&#xff0c;全称是Internet Communications Engine。 它的主要设计目标是&#xff1a; • 提供适用于异种环境的面向对象中间件平台…

linux下编译jrtplib-3.9.1

网站&#xff1a;http://blog.csdn.net/caoshangpa/article/details/51416822 一、下载jrtplib、jthread、CMake jrtplib&#xff1a;http://research.edm.uhasselt.be/jori/jrtplib/jrtplib-3.9.1.zip jthread&#xff1a;http://research.edm.uhasselt.be/jori/jthread/jthre…

mysql从入门到精通之数据库基本概念理解

生活中的记账&#xff0c;帐&#xff1a;就是数据&#xff0c;或者简单理解为信息吧。记账&#xff1a;就是存储数据、信息生活中记账都是记在哪儿呢&#xff1f;比如&#xff1a;门上、墙上、日历上无论记在哪儿&#xff1f;特点&#xff1a;记录的都是信息&#xff0c;变化的…

windbg 常用命令详解

&#xfeff;&#xfeff;一、 1、 !address eax 查看对应内存页的属性 2、 vertarget 显示当前进程的大致信息 3 !peb 显示process Environment Block 4、 lmvm 可以查看任意一个dll的详细信息 例如&#xff1a;我们查看cyusb.sys的信息 5.reload !sym 加载符号文件 6…

JS 与Flex交互:html中的js 与flex中的actionScript通信

Flex与JavaScript交互的问题&#xff0c;这里和大家分享一下&#xff0c;主要包括Flex调用JavaScript中的函数和JavaScript调用Flex中的函数两大部分内容。 Flex 与JavaScript 交互&#xff0c;主要依靠Flex的ExternalInterface&#xff0c;其提供了addCallBack和call方法。 一…

poj3615

floyd水题&#xff0c;将map[i][j]理解为从i到j的路径中高度最小即可&#xff0c;将松弛条件改为map[i][j]MIN(map[i][j],MAX(map[i][k],map[k][j]));好好理解一下吧 View Code 1 #include <stdio.h> 2 #include <string.h> 3 #define MAXN 310 4 #define INF (1&…

H264码流打包分析(精华)

网页&#xff1a;https://www.cnblogs.com/lidabo/p/4602422.htmlH264码流打包分析SODB 数据比特串&#xff0d;&#xff0d;&#xff1e;最原始的编码数据RBSP 原始字节序列载荷&#xff0d;&#xff0d;&#xff1e;在SODB的后面填加了结尾比特&#xff08;RBSP trailing b…

postman安装和简单使用

postman 模拟向接口发送请求&#xff0c;测试接口 下载 https://www.getpostman.com/downloads/ 使用 朝地址发请求&#xff0c;拿到json格式的数据 想看的层次更分明可以上网搜json 给后端发送数据 转载于:https://www.cnblogs.com/zhengyuli/p/11117632.html

QC使用流程(1)之安装篇

1、准备环境 1.1、安装操作系统 本次教程使用的操作系统是Windows Server 2003&#xff0c;安装在虚拟机6.5上。 1.2、安装数据库 本次教程使用的数据库是Microsoft SQL Server 2000 简体中文企业版 SP4升级补丁 具体安装步骤如下&#xff1a; 1)、打开数据库安装程序&#xff…

Java连载1-概述常用的dos命令

本想写完那两个再开始新的&#xff0c;然而客观条件不允许&#xff0c;之前从未接触过Java&#xff0c;从零开始吧​&#xff01;&#xff01;&#xff01; 一、概述 C盘下​&#xff1a;programme file 一般为64位程序安装的目录&#xff0c;programme file&#xff08;X86&am…

SQL 查询--日期条件(今日、昨日、本周、本月。。。) (转)

主要用到sql 函数 DATEDIFF(datepart,startdate,enddate) sql 语句&#xff0c;设 有 数据库表 tableA&#xff08;日期字段ddate&#xff09; ——查询 今日 select * from tableA where DateDiff(dd,VoucherDate,getdate()) 0 ——查询 昨日 select * from tableA where …