Mocking framework
[译] 什么是Mocking framework?它有什么用?
原位地址:http://codetunnel.com/blog/post/what-is-a-mocking-framework-why-is-it-useful
今天我想讲下关于mocking frameworks,并且解释下他为什么有用处。我将给你们展示用和不用mocking framework两种测试方法。
假设我们已经有了一个Driver类:
- public class Driver
- {
- private IVehicle vehicleToDrive;
- public Driver(IVehicle vehicleToDrive)
- {
- this.vehicleToDrive = vehicleToDrive;
- }
- public bool EvasiveManeuvers(bool alertOffendingDriver)
- {
- bool success = false;
- if (alertOffendingDriver)
- success = this.vehicleToDrive.ApplyBrakes() && this.vehicleToDrive.HonkHorn();
- else
- success = this.vehicleToDrive.ApplyBrakes();
- return success;
- }
- }
【注意】Driver类的构造函数依赖IVehicle接口!定义如下:
- public interface IVehicle
- {
- ///<summary>
- ///Honks the vehicle's horn.
- ///</summary>
- ///<returns>
- ///True if the action was successful.
- ///</returns>
- bool HonkHorn();
- ///<summary>
- ///Applies the vehicle's brakes.
- ///</summary>
- ///<returns>
- ///True if the action was successful.
- ///</returns>
- bool ApplyBrakes();
- }
现在我们需要写2个单元测试来测试Driver。然而在我们写之前,必须能够通过Driver的构造函数才行,他依赖IVehicle接口。
很多人认为只要随便实现IVehicle就可以了,这当然没问题。事实上,如果你完成接口IVechicle,你也不用写单元测试了。
单元测试是孤立性的,测试Driver类并且还要实现一个完整IVehicle实现不是单元测试,那是整体封闭测试。如果你在测试时出错了,你不能确认是Driver出错了还是其他的类。
我们现在搞一个假的IVehicle的实现来解决这个依赖问题。
- public class FakeVehicle : IVehicle
- {
- public int CalledHonkHorn = 0;
- public int CalledApplyBrakes = 0;
- public bool HonkHorn()
- {
- this.CalledHonkHorn++;
- return true;
- }
- public bool ApplyBrakes()
- {
- this.CalledApplyBrakes++;
- return true;
- }
- }
注意我们定义的两个int成员,在我们的单元测试中我们将用他们判断HonkHorn()和ApplyBrakes()的调用情况。
现在我们可以写单元测试了,我们将测试两个行为:
1、Can_Evade_Trouble
2、Can_Evade_Trouble_And_Alert_Offending_Driver
- [TestMethod]
- public void Can_Evade_Trouble()
- {
- // Arrange (set up a scenario)
- FakeVehicle fakeVehicle = new FakeVehicle();
- Driver target = new Driver(fakeVehicle);
- // Act (attempt the operation)
- bool success = target.EvasiveManeuvers(false);
- // Assert (verify the result)
- Assert.IsTrue(success);
- Assert.IsTrue(fakeVehicle.CalledHonkHorn == 0);
- Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);
- }
- [TestMethod]
- public void Can_Evade_Trouble_And_Alert_Offending_Driver()
- {
- // Arrange (set up a scenario)
- FakeVehicle fakeVehicle = new FakeVehicle();
- Driver target = new Driver(fakeVehicle);
- // Act (attempt the operation)
- bool success = target.EvasiveManeuvers(true);
- // Assert (verify the result)
- Assert.IsTrue(success);
- Assert.IsTrue(fakeVehicle.CalledHonkHorn == 1);
- Assert.IsTrue(fakeVehicle.CalledApplyBrakes == 1);
- }
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就可以了。
现在我们重写上面的测试代码,回头再解释它的牛逼的地方。
- [TestMethod]
- public void Can_Evade_Trouble()
- {
- // Arrange (set up a scenario)
- Mock<IVehicle> mock = new Mock<IVehicle>();
- mock.Setup(x => x.ApplyBrakes()).Returns(true);
- Driver target = new Driver(mock.Object);
- // Act (attempt the operation)
- bool success = target.EvasiveManeuvers(false);
- // Assert (verify the result)
- Assert.IsTrue(success);
- mock.Verify(x => x.HonkHorn(), Times.Never());
- mock.Verify(x => x.ApplyBrakes(), Times.Once());
- }
- [TestMethod]
- public void Can_Evade_Trouble_And_Alert_Offending_Driver()
- {
- // Arrange (set up a scenario)
- Mock<IVehicle> mock = new Mock<IVehicle>();
- mock.Setup(x => x.HonkHorn()).Returns(true);
- mock.Setup(x => x.ApplyBrakes()).Returns(true);
- Driver target = new Driver(mock.Object);
- // Act (attempt the operation)
- bool success = target.EvasiveManeuvers(true);
- // Assert (verify the result)
- Assert.IsTrue(success);
- mock.Verify(x => x.HonkHorn(), Times.Once());
- mock.Verify(x => x.ApplyBrakes(), Times.Once());
- }
不管你信不信,反正我们可以丢掉FakeVehicle类了。
Moq动态的构造了接口的实现类,所有的成员默认的值都是其默认值。
由于bool类型的默认值是false,所以HonkHorn()和ApplyBrakes()在mock.Object实例中都将返回false,显然我希望返回true的,所以用Moq的Setup()方法来解决。
Setup参数是一个lambda表达式,可以强类型的方式直接访问到其成员。例如
- mock.Setup(x=>x.HonkHorn().Returns(true));
如果不用lambda用字符串,类似mock.Setup("HonkHorn").Returns(true),这种方式比较丑,如果接口变化了这边就该报错了。
moq用lambda就是保证所有的访问都是强类型的。
另外如果你的方法接受一些参数比如string例如
- mock.Setup(x => x.HonkHorn("loudly");
如果这个值不是必须的(不是这个值就不能通过),那就可以用It类代替,他包含很多有用的方法。下面的例子就是接受任意字符串
- 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。
- //Do this
- Mock<IVehicle> mock = new Mock<IVehicle>();
- Driver driver = new Driver(mock.Object);
- //Not this
- Mock<IVehicle> mock = new Mock<IVehicle>();
- Driver driver = new Driver(mock);
现在回过头看看我们的moq的单元测试,所有的结果都是对的。调用次数也是对的,moq能自动记录方法的调用次数,我们只需要调用mock.Verify(),然后通过lambda表达式就能验证我们想要的次数是否正确。
这里是最基础的moq用法,希望你现在能够明白mockingframework的用处并明白moq怎么完成工作的。

Mocking framework的更多相关文章
- Testing with a mocking framework (EF6 onwards)
When writing tests for your application it is often desirable to avoid hitting the database. Entity ...
- 什么是Mocking framework?它有什么用?
原位地址:http://codetunnel.com/blog/post/what-is-a-mocking-framework-why-is-it-useful 今天我想讲下关于mocking fr ...
- 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 ...
- 什么是Mocking framework?它有什么用?(转)
今天我想讲下关于mocking frameworks,并且解释下他为什么有用处.我将给你们展示用和不用mocking framework两种测试方法. 假设我们已经有了一个Driver类: publi ...
- Googletest - Google Testing and Mocking Framework
Googletest - Google Testing and Mocking Framework https://github.com/google/googletest
- 利用Mocking Framework 单元测试Entity Framework
一.前言 在实际编写程序时,往往需要与数据库打交道,在单元测试中直接使用数据库又显得太重,如果可以方便的编写一些测试数据,这样更易于检测功能.如何模拟数据库行为便是本篇的主题.微软有教程说明Moq E ...
- JustMock Lite (Free Mocking Framework For .net)
通过 Nuget 安装 2. 官网下载(官网不行点这里) 3. 帮助文档 商业版和免费版区别概览 MockingContainer 测试类准备:一般来说也是业务类 public class C ...
- 第一篇:Entity Framework 简介
先从ORM说起吧,很多年前,由于.NET的开源组件不像现在这样发达,更别说一个开源的ORM框架,出于项目需要,以及当时OOP兴起(总不至于,在项目里面全是SQL语句),就自己开始写ORM框架.要开发O ...
- Entity Framework版本历史概览
转自:http://www.cnblogs.com/fecktty2013/archive/2014/09/26/entityframework-overview.html EF版本 .net fra ...
随机推荐
- Linux-常用命令1---对文件进行查看、复制、移动和分割
基于Linux的操作系统是一种自由和开放源代码的类UNIX操作系统. Linux的几大特点决定了它的不可代替和无法超越性: (1)免费的/开源的:(2)支持多线程/多用户: (3)安全性好; (4)对 ...
- Asp.net MVC + EF + Spring.Net 项目实践(四)
这篇写一写如何使用Spring.net来解耦各个项目 1. 在接口层添加IStudentBLL文件,里面有GetStudent和GetAllStudents两个方法:然后在StudentBLL类里实现 ...
- Asp.Net MVC5入门学习系列①
原文:Asp.Net MVC5入门学习系列① 现在直接开始MVC5的学习系列,学习资源来自Micrsoft. 开始使用Asp.Net MVC 5 打开Visual Studio 2013,然后新建一个 ...
- RegularExpressionValidator控件
原文:RegularExpressionValidator控件 ★搜Asp.net★(www.soAsp.net),为专业技术文档网站.包括Asp.net开发技术文档·C#开发技术文档·Access/ ...
- hdu2899 Strange fuction
在区间(0,100).在恒大二阶导数0.f(x)有极小值.用的最低要求的一阶导数值点: #include<math.h> #include<stdio.h> #include& ...
- Tomcat集群+Nginx+Redis服务搭建
由于公司新业务突然上来了,单个Tomcat实例已经不能满足业务发展的需要了,只能通过搭建集群来解决问题了.所以就出现了下面的内容: 1.Redis保存Session信息 为了保存Session信息在集 ...
- 强大的jquery-制作选项卡
最近在学习jquery,特地把今天写的一个选项卡源码贴出来.只是做只是梳理,大神们请不要吐槽,如果有更好的方法,欢迎指点.谢谢. css <style> #tab div{ width:2 ...
- javascript this指针指向?
前言 理解javascript的指针就需要先了解js的执行环境和作用域!执行环境的定义了变量或函数有权访问的其他数据,决定了它们各自的行为.每个执行环境都有一个与之关联的变量对象,环境中定义的所有的变 ...
- 在Android中自动实现横竖屏切换的问题
http://developer.android.com/training/basics/supporting-devices/screens.html参照Google推荐的做法 在你项目的res 文 ...
- 利用Delphi监视注册表的变化
转帖:利用Delphi监视注册表的变化 2009-12-23 11:53:51 分类: 利用Delphi监视注册表的变化 我们在编写软件的时候,常常需要把一些信息保存到系统的注册表中.如果 ...