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属性。让我们看看下边的例子:

  1. //define interface to be mocked
  2.  
  3. public interface IFake
  4. {
  5.  
  6. bool DoSomething(string actionname);
  7.  
  8. }
  9.  
  10. //define the test method
  11.  
  12. [TestMethod]
  13.  
  14. public void Test_Interface_IFake()
  15. {
  16.  
  17. //make a mock Object by Moq
  18.  
  19. var mo = new Mock<IFake>();
  20.  
  21. //Setup our mock object
  22.  
  23. mo.Setup(foo => foo.DoSomething("Ping"))
  24.  
  25. .Returns(true);
  26.  
  27. //Assert it!
  28.  
  29. Assert.AreEqual(true, mo.Object.DoSomething("Ping"));
  30. }

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

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

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

It

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

  1. public interface IEmailSender
  2. {
  3.  
  4. bool Send(string subject, string body, string email);
  5.  
  6. }
  7. [TestMethod]
  8.  
  9. public void User_Can_Send_Password()
  10. {
  11.  
  12. var emailMock = new Mock<IEmailSender>();
  13.  
  14. emailMock
  15.  
  16. .Setup(sender => sender.Send(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
  17.  
  18. .Returns(true);
  19.  
  20. }

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

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

  1. var productRepository = new Mock<IProductRepository>();
  2.  
  3. productRepository
  4.  
  5. .Expect(p => p.Get(It.Is<int>(id => id > 0 && id < 6)))
  6.  
  7. .Returns(newProduct.Object);

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

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

MockBehavior

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

  1. namespace Moq
  2. {
  3. // Summary:
  4. // Options to customize the behavior of the mock.
  5. public enum MockBehavior
  6. {
  7. // Summary:
  8. // Causes the mock to always throw an exception for invocations that don't have
  9. // a corresponding setup.
  10. Strict = 0,
  11. //
  12. // Summary:
  13. // Will never throw exceptions, returning default values when necessary (null
  14. // for reference types, zero for value types or empty enumerables and arrays).
  15. Loose = 1,
  16. //
  17. // Summary:
  18. // Default mock behavior, which equals Moq.MockBehavior.Loose.
  19. Default = 1,
  20. }
  21. }

现在,看看如下例子:

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

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

MockFactory

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

  1. var factory = new MockFactory(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock };
  2.  
  3. // Create a mock using the factory settings
  4.  
  5. var fooMock = factory.Create<IFake>();
  6.  
  7. // Create a mock overriding the factory settings
  8.  
  9. var barMock = factory.Create<IEmailSender>(MockBehavior.Loose);
  10.  
  11. // Verify all verifiable expectations on all mocks created through the factory
  12.  
  13. factory.Verify();

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

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

Verification

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

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

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

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

  1. [TestMethod]
  2.  
  3. public void Test_FindByName_GetCalled()
  4. {
  5.  
  6. // create some mock data
  7.  
  8. IList<Product> products = new List<Product>
  9.  
  10. {
  11.  
  12. new Product { ProductId = 1, Name = "C# Unleashed",
  13.  
  14. Description = "Short description here", Price = 49.99 },
  15.  
  16. new Product { ProductId = 2, Name = "ASP.Net Unleashed",
  17.  
  18. Description = "Short description here", Price = 59.99 },
  19.  
  20. new Product { ProductId = 3, Name = "Silverlight Unleashed",
  21.  
  22. Description = "Short description here", Price = 29.99 }
  23.  
  24. };
  25.  
  26. Mock<IProductRepository> mock = new Mock<IProductRepository>();
  27.  
  28. //mock
  29.  
  30. //.Setup(sender => sender.FindById(It.IsAny<int>()))
  31.  
  32. //.Returns((int s) => products.Where(
  33.  
  34. // x => x.ProductId == s).Single());
  35.  
  36. mock.Object.FindById(1);
  37.  
  38. mock
  39.  
  40. .Verify(x => x.FindById(1), Times.Once());
  41.  
  42. }
  43. }

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

首先是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. winds引导配置数据文件包含的os项目无效

    我装了winds7与linux双系统,用easyBcd程序时,删除了一个winds7,之后winds7就进不去了, 进入winds7时显示winds未能启动,原因可能是最近更改了硬件或软件.解决此问题 ...

  2. [置顶] android 自定义圆角ImageView以及锯齿的处理

    看到很多人开发过程中要使用圆角图片时,解决方法有: 1.重新绘制一张图片 2.通过布局来配置 3.通过重写View来实现 其中1,2在这里就不讲了,重点讲讲方法三的实现. 实现一:通过截取画布一个圆形 ...

  3. SendMessage用法实例

    转: http://blog.csdn.net/coolszy/article/details/5523700 SendMessage用法 windowsbuttonmenucommandlistc# ...

  4. Archlinux 安装配置指导 2015-05-24

    因为用的Linode VPS的系统是Archlinux的,想在本地弄个系统做测试用,这样比较方便.然后发现自己在6年前做的一个Archlinux 安装配置Flash,好怀念的赶脚. 时过进迁,没想到A ...

  5. A - 数塔

    A - 数塔 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  6. BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )

    BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ---------------------------------------------------- ...

  7. R与数据分析旧笔记(六)多元线性分析 上

    > x=iris[which(iris$Species=="setosa"),1:4] > plot(x) 首先是简单的肉眼观察数据之间相关性 多元回归相较于一元回归的 ...

  8. 页面打开直接执行a点击事件

    <script> window.onload = function(){ function autoclick(){ var url = document.getElementById(' ...

  9. HTTP 错误 401.3 - Unauthorized由于 Web 服务器上此资源的访问控制列表(ACL)解决办法

    对应站点目录的IUSR的权限没设造成的...在属性——>安全——> 高级 中把IUSR用户找出来添加好就OK了 注:IUSR(匿名访问 Internet 信息服务的内置帐户)

  10. python xlrd对excel的读取功能

    工作簿 xlrd.open_workbook('test.xls') workbook.dump() workbook.nsheets workbook.sheets() workbook.sheet ...