今天我想讲下关于mocking frameworks,并且解释下他为什么有用处。我将给你们展示用和不用mocking framework两种测试方法。

假设我们已经有了一个Driver类:

  1. public class Driver
  2. {
  3. private IVehicle vehicleToDrive;
  4. public Driver(IVehicle vehicleToDrive)
  5. {
  6. this.vehicleToDrive = vehicleToDrive;
  7. }
  8. public bool EvasiveManeuvers(bool alertOffendingDriver)
  9. {
  10. bool success = false;
  11. if (alertOffendingDriver)
  12. success = this.vehicleToDrive.ApplyBrakes() && this.vehicleToDrive.HonkHorn();
  13. else
  14. success = this.vehicleToDrive.ApplyBrakes();
  15. return success;
  16. }
  17. }

【注意】Driver类的构造函数依赖IVehicle接口!定义如下:

  1. public interface IVehicle
  2. {
  3. ///<summary>
  4. ///Honks the vehicle's horn.
  5. ///</summary>
  6. ///<returns>
  7. ///True if the action was successful.
  8. ///</returns>
  9. bool HonkHorn();
  10. ///<summary>
  11. ///Applies the vehicle's brakes.
  12. ///</summary>
  13. ///<returns>
  14. ///True if the action was successful.
  15. ///</returns>
  16. bool ApplyBrakes();
  17. }

  现在我们需要写2个单元测试来测试Driver。然而在我们写之前,必须能够通过Driver的构造函数才行,他依赖IVehicle接口。
很多人认为只要随便实现IVehicle就可以了,这当然没问题。事实上,如果你完成接口IVechicle,你也不用写单元测试了。
单元测试是孤立性的,测试Driver类并且还要实现一个完整IVehicle实现不是单元测试,那是整体封闭测试。如果你在测试时出错了,你不能确认是Driver出错了还是其他的类。

我们现在搞一个假的IVehicle的实现来解决这个依赖问题。

  1. public class FakeVehicle : IVehicle
  2. {
  3. public int CalledHonkHorn = 0;
  4. public int CalledApplyBrakes = 0;
  5. public bool HonkHorn()
  6. {
  7. this.CalledHonkHorn++;
  8. return true;
  9. }
  10. public bool ApplyBrakes()
  11. {
  12. this.CalledApplyBrakes++;
  13. return true;
  14. }
  15. }

注意我们定义的两个int成员,在我们的单元测试中我们将用他们判断HonkHorn()和ApplyBrakes()的调用情况。

现在我们可以写单元测试了,我们将测试两个行为:
1、Can_Evade_Trouble
2、Can_Evade_Trouble_And_Alert_Offending_Driver

  1. [TestMethod]
  2. public void Can_Evade_Trouble()
  3. {
  4. // Arrange (set up a scenario)
  5. FakeVehicle fakeVehicle = new FakeVehicle();
  6. Driver target = new Driver(fakeVehicle);
  7. // Act (attempt the operation)
  8. bool success = target.EvasiveManeuvers(false);
  9. // Assert (verify the result)
  10. Assert.IsTrue(success);
  11. Assert.IsTrue(fakeVehicle.CalledHonkHorn == 0);
  12. Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);
  13. }
  14. [TestMethod]
  15. public void Can_Evade_Trouble_And_Alert_Offending_Driver()
  16. {
  17. // Arrange (set up a scenario)
  18. FakeVehicle fakeVehicle = new FakeVehicle();
  19. Driver target = new Driver(fakeVehicle);
  20. // Act (attempt the operation)
  21. bool success = target.EvasiveManeuvers(true);
  22. // Assert (verify the result)
  23. Assert.IsTrue(success);
  24. Assert.IsTrue(fakeVehicle.CalledHonkHorn == 1);
  25. Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);
  26. }

OK,现在我们成功的通过单元测试。他们的EvasiveManeuvers()方法都返回true,并且IVechicle.ApplyBrakes()方法每次都被调用了,HonkHorn()方法第一个测试没有被调用,第二次调用了。
注意,在真正的测试驱动开发 TDD(Test Driven Development)我们首先要写测试,然后才是写代码再测试,但我们没这么做。

这是一个比较不太讨人喜欢的写测试的风格,为了通过Driver我们写了一个FakeVehicle,假如需要写很多这种类那就麻烦了。

为了解决这种问题,Mocking Framework诞生了。

我们现在用一个叫Moq的框架(https://code.google.com/p/moq/),他的语法独特,但用过之后你会感到写起来很流畅。只要把moq.dll添加到引用,然后using Moq就可以了。

现在我们重写上面的测试代码,回头再解释它的牛逼的地方。

  1. [TestMethod]
  2. public void Can_Evade_Trouble()
  3. {
  4. // Arrange (set up a scenario)
  5. Mock<IVehicle> mock = new Mock<IVehicle>();
  6. mock.Setup(x => x.ApplyBrakes()).Returns(true);
  7. Driver target = new Driver(mock.Object);
  8. // Act (attempt the operation)
  9. bool success = target.EvasiveManeuvers(false);
  10. // Assert (verify the result)
  11. Assert.IsTrue(success);
  12. mock.Verify(x => x.HonkHorn(), Times.Never());
  13. mock.Verify(x => x.ApplyBrakes(), Times.Once());
  14. }
  15. [TestMethod]
  16. public void Can_Evade_Trouble_And_Alert_Offending_Driver()
  17. {
  18. // Arrange (set up a scenario)
  19. Mock<IVehicle> mock = new Mock<IVehicle>();
  20. mock.Setup(x => x.HonkHorn()).Returns(true);
  21. mock.Setup(x => x.ApplyBrakes()).Returns(true);
  22. Driver target = new Driver(mock.Object);
  23. // Act (attempt the operation)
  24. bool success = target.EvasiveManeuvers(true);
  25. // Assert (verify the result)
  26. Assert.IsTrue(success);
  27. mock.Verify(x => x.HonkHorn(), Times.Once());
  28. mock.Verify(x => x.ApplyBrakes(), Times.Once());
  29. }

不管你信不信,反正我们可以丢掉FakeVehicle类了。
Moq动态的构造了接口的实现类,所有的成员默认的值都是其默认值。
由于bool类型的默认值是false,所以HonkHorn()ApplyBrakes()在mock.Object实例中都将返回false,显然我希望返回true的,所以用Moq的Setup()方法来解决。

Setup参数是一个lambda表达式,可以强类型的方式直接访问到其成员。例如

  1. mock.Setup(x=>x.HonkHorn().Returns(true));

如果不用lambda用字符串,类似mock.Setup("HonkHorn").Returns(true),这种方式比较丑,如果接口变化了这边就该报错了。
moq用lambda就是保证所有的访问都是强类型的。

另外如果你的方法接受一些参数比如string例如

  1. mock.Setup(x => x.HonkHorn("loudly");

如果这个值不是必须的(不是这个值就不能通过),那就可以用It类代替,他包含很多有用的方法。下面的例子就是接受任意字符串

  1. mock.Setup(x => x.HonkHorn(It.IsAny<string>())).Returns(true);

Moq可以让你创建任意类型T的Mock<T>实例,然后调用Setup()去设置属性或方法的返回值,随便什么值只要是为你达到测试的目的。加入你需要一个属性返回一个集合,你只需要定义好集合类,并且通过Setup()的Returns方法返回集合就行了。当这个mock.Object的集合属性被访问时就会返回你定义好的集合。
记住:1、mock的对象不是你的测试,你mock的对象是让你能够通过他们进入你要测试的类/组件。
另外注意 var mock = new Mock<T>。mock可不是T的实例,mock是Mock<T>的对象实例,T的实例是mock.Object。所以不要搞混了;实例化Driver时要用mock.Object。

  1. //Do this
  2. Mock<IVehicle> mock = new Mock<IVehicle>();
  3. Driver driver = new Driver(mock.Object);
  4. //Not this
  5. Mock<IVehicle> mock = new Mock<IVehicle>();
  6. Driver driver = new Driver(mock);

现在回过头看看我们的moq的单元测试,所有的结果都是对的。调用次数也是对的,moq能自动记录方法的调用次数,我们只需要调用mock.Verify()然后通过lambda表达式就能验证我们想要的次数是否正确。

这里是最基础的moq用法,希望你现在能够明白mockingframework的用处并明白moq怎么完成工作的。

什么是Mocking framework?它有什么用?(转)的更多相关文章

  1. Testing with a mocking framework (EF6 onwards)

    When writing tests for your application it is often desirable to avoid hitting the database.  Entity ...

  2. 什么是Mocking framework?它有什么用?

    原位地址:http://codetunnel.com/blog/post/what-is-a-mocking-framework-why-is-it-useful 今天我想讲下关于mocking fr ...

  3. Mocking framework

    [译] 什么是Mocking framework?它有什么用? 原位地址:http://codetunnel.com/blog/post/what-is-a-mocking-framework-why ...

  4. What is a mocking framework? Why is it useful?

    Today I want to talk about mocking frameworks and why they are useful. In order to do that I first n ...

  5. Googletest - Google Testing and Mocking Framework

    Googletest - Google Testing and Mocking Framework https://github.com/google/googletest

  6. 利用Mocking Framework 单元测试Entity Framework

    一.前言 在实际编写程序时,往往需要与数据库打交道,在单元测试中直接使用数据库又显得太重,如果可以方便的编写一些测试数据,这样更易于检测功能.如何模拟数据库行为便是本篇的主题.微软有教程说明Moq E ...

  7. JustMock Lite (Free Mocking Framework For .net)

    通过 Nuget 安装 2.   官网下载(官网不行点这里) 3.   帮助文档 商业版和免费版区别概览 MockingContainer 测试类准备:一般来说也是业务类 public class C ...

  8. 第一篇:Entity Framework 简介

    先从ORM说起吧,很多年前,由于.NET的开源组件不像现在这样发达,更别说一个开源的ORM框架,出于项目需要,以及当时OOP兴起(总不至于,在项目里面全是SQL语句),就自己开始写ORM框架.要开发O ...

  9. Entity Framework版本历史概览

    转自:http://www.cnblogs.com/fecktty2013/archive/2014/09/26/entityframework-overview.html EF版本 .net fra ...

随机推荐

  1. cbuffer padding

    nx glslc float 起始于 内存位置4x0 ,4x1,4x2 ,4x3.... bit float2 起始于 内存位置2x4x0 ,2x4x1,2x4x2 ,2x4x3.... bit fl ...

  2. .Net程序测试阿里云OSS开放存储服务

    阿里云官网有提供OSS相关的操作API文档和.Net程序的 SDK,也可以在这里下载OSS相关文件 但是API文档里面的都是通过http请求和响应的消息来描述如何操作OSS的 而一般在程序中需要的是O ...

  3. 2017.3.31 spring mvc教程(八) <mvc:annotation-driven />所做的工作

    学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...

  4. 开源jabber(XMPP)架设内部即时通讯服务的解决方案

    Jabber 是著名的即时通讯服务服务器,它是一个自由开源软件,能让用户自己架即时通讯服务器,可以在Internet上应用,也可以在局域网中应用.    XMPP(可扩展消息处理现场协议)是基于可扩展 ...

  5. 仿苹果电脑任务栏菜单&&拼图小游戏&&模拟表单控件

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. MySQL5.7 基于二进制包的安装

    1.MySQL5.7安装注意事项 1.在MySQL5.7中mysql_install_db已经不再推荐使用,建议改成mysqld-initialize 完成实力初始化.(mysql_install_d ...

  7. RFS 理解

    1.背景 网卡接收一个数据包的情况下,会经过三个阶段:   - 网卡产生硬件中断通知CPU有包到达 - 通过软中断处理此数据包 - 在用户态程序处理此数据包   在SMP体系下,这三个阶段有可能在3个 ...

  8. xtrabackup详解

    xtrabackup是Percona公司CTO Vadim参与开发的一款基于InnoDB的在线热备工具,具有开源,免费,支持在线热备,备份恢复速度快,占用磁盘空间小等特点,并且支持不同情况下的多种备份 ...

  9. Laravel之Eloquent ORM关联

    一.一对一 1.主对从(hasOne) 从User模型中取出用户的手机 User模型中: /** * 获取关联到用户的手机 */ public function phone() { return $t ...

  10. MySQL 事件EVENT

    一.用途用于某一时间执行一个事件或周期性执行一个事件. 二.语法CREATE [DEFINER = { user | CURRENT_USER }] EVENT [IF NOT EXISTS] eve ...