From: http://www.cnblogs.com/techborther/archive/2012/01/10/2317998.html


前几天调查完了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://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

ASP.Net MVC3 - The easier to run Unit Tests by moq #Reprinted#的更多相关文章

  1. [Java in NetBeans] Lesson 07. JavaDoc and Unit Tests

    这个课程的参考视频和图片来自youtube. 主要学到的知识点有: 1. organize code into packages Create a package if want to make th ...

  2. CKEditor Html Helpers for ASP.NET MVC3 Razor/WebForms Views

    一.原生方法: 在 razor 中 使用Fckeditor 编辑内容,需要引入js <script src="@Url.Content("~/fckeditor/fckedi ...

  3. [转]Creating Unit Tests for ASP.NET MVC Applications (C#)

    本文转自:http://www.asp.net/mvc/tutorials/older-versions/unit-testing/creating-unit-tests-for-asp-net-mv ...

  4. Log4Net异常日志记录在asp.net mvc3.0的应用

    前言 log4net是.Net下一个非常优秀的开源日志记录组件.log4net记录日志的功能非常强大.它可以将日志分不同的等级,以不同的格式,输出到不同的媒介.本文主要是简单的介绍如何在Visual ...

  5. ASP.NET MVC3 Razor 调试与预加载

    目录(?)[-] 获取服务器信息 FormsAuthenticationSlidingExpiration 属性 MVC3预加载   在ASP.NET MVC3开发中,调试中怎么也是不可缺少的,那对于 ...

  6. 在没有安装有mvc3的主机上部署asp.net mvc3网站,需要包含的DLL文件

    原文:在没有安装有mvc3的主机上部署asp.net mvc3网站,需要包含的DLL文件 http://hi.baidu.com/aspxdiyer/blog/item/5515a69943232f1 ...

  7. ASP.NET MVC3的学习

    ASP.NET MVC第一次课(2013-12-25晚学完)     1.ASP.NET MVC 的特点       分离任务          可扩展        强大的URL重写(路由)机制   ...

  8. ASP.NET MVC3 Model验证总结

    ASP.NET MVC3中的Model是自验证的,这是通过.NET4的System.ComponentModel.DataAnnotations命名空间完成的. 我们要做的只是给Model类的各属性加 ...

  9. [转]ASP.NET MVC3 + EF 性能优化解决方案以及最优架构

    [集思广议]      我们用 asp.net mvc3 + ef 做了一个网站,现在是内测阶段,发现打开速度非常慢.首页打开(无缓存)都在5-6s以上(测试环境:程序和db都在本机),请问各位 mv ...

随机推荐

  1. C#中经常使用的几种读取XML文件的方法

    XML文件是一种经常使用的文件格式,比如WinForm里面的app.config以及Web程序中的web.config文件,还有很多重要的场所都有它的身影.Xml是Internet环境中跨平台的,依赖 ...

  2. Dyanmics CRM您无法登陆系统。原因可能是您的用户记录或所属的业务部门在Microoft Dynamics CRM中已被禁用

    当在操作CRM时,做不论什么的写操作包含创建数据.更新数据.都会提示以下截图中的错误:"您无法登陆系统.原因可能是您的用户记录或所属的业务部门在Microoft Dynamics CRM中已 ...

  3. JavaScript之Ajax

    简介:AJax是在2005年被Adaptive Path的Jesse James Garrett发明的,用户概括异步加载页面内容的技术---说通俗点,Ajax就是实现局部页面通过JavaScript与 ...

  4. 2014.8.20break,continue,字符串,数字和日期

    (一)break与continue break——彻底终断循环 continue——中断本次循环,继续下次循环 break举例: //求100以内所有质数 ; i <= ; i++) { ;// ...

  5. [转载]VMWare网络连接透析

    http://blog.csdn.net/struggleyb/article/details/1102214 以前在学校,VMWare里面的Gentoo Linux是采用network bridge ...

  6. 关于多线程中GCD的使用

    GCD 分为异步和同步 异步: ```objc  dispatch_async (  参数1  , {      } 同步: dispatch_sync( 参数1   , {   } ``` ###参 ...

  7. JavaSE复习日记 : 多态

    /** * 里氏替换原则 : * 能使用父类的地方,一定可以使用子类 * 什么是多态 : * 父类的引用,指向子类的对象 * 多态的前提条件 : * 有继承关系的两个类 * 多态的目的 : * ☆☆☆ ...

  8. 堆分配与栈分配---SAP C++电面(5)/FEI

    一直以来总是对这个问题的认识比较朦胧,我相信很多朋友也是这样的,总是听到内存一会在栈上分配,一会又在堆上分配,那么它们之间到底是怎么的区别呢?为了说明这个问题,我们先来看一下内存内部的组织情况. 从上 ...

  9. lua math libary

    函数名 描述 示例 结果 pi 圆周率 math.pi 3.1415926535898 abs 取绝对值 math.abs(-2012) 2012 ceil 向上取整 math.ceil(9.1) 1 ...

  10. 一周学会Mootools 1.4中文教程:(6)动画

    先看一下动画的参数设置: 参数: fps - (number:默认是50) 每秒的帧数. unit - (string:默认是 false) 单位,可为 'px','em',或 '%'. link - ...