.NET Mocking Framework对比

摘要:
当前版本支持。NET 3.5&Lambda表达式、泛型和扩展方法用于实现调用API的强类型。h.TouchIron(null))。约束(Is。匹配<Iron>嘴。预期(m=>口。验证所有预期();

  单元测试中,为了让单元测试程序完全脱离外部依赖,需要使用到Mock对象和Stub对象。虽然可以手工编写Mock对象和Stub对象,但通常我们都使用Mocking Framework来帮助我们简单快速的构建需要的Mock对象以及Stub对象。

一、概述

  常见的Mocking Framework有如下几种:  

  1、Rhino Mocks V3.6(2009-9-1)

  Rhino Mocks是由Ayende Rahien 开发的一个开源项目,目前版本支持.NET 3.5 & 4.0以及Silverlight的CLR, 采用Castle DynamicProxy方式实现Mock对象的构建。

  采用Lambda表达式和泛型以及扩展方法实现调用API的强类型化。

  因为实现方式的限制,只能对Interface以及可继承的类进行Mock处理,对于sealed类、非virtual,非抽象以及静态方法则无能为力。

  因为考虑与旧版本的兼容性的问题, API较为繁琐,冗余方法较多,学习使用时容易混淆。

  被Mvccontrib项目所支持,可以被该项目的TestHelper调用,并用于ASP.NET中Stub对象的模拟。

  轻量级应用,不需要被安装,只需要在测试项目中进行引用即可实现对其的调用。

.NET Mocking Framework对比第1张.NET Mocking Framework对比第2张代码
    [TestFixture]
    
public class BrainTests
    {
        
/// <summary>
        
/// Verify that if hand throws an exception having touched a hot iron,  <see cref="IMouth.Yell"/> gets called.
        
/// </summary>
        
/// <remarks>
        
/// Rhino Mocks can mock both interfaces and classes - however, only virtual methods 
        
/// of a class can be mocked (try changing IHand/IMouth to Hand/Mouth).
        
/// </remarks>
        [Test]
        
public void TouchHotIron_Yell()
        {
            var hand 
= MockRepository.GenerateStub<IHand>();
            var mouth 
= MockRepository.GenerateMock<IMouth>();
            hand.Stub(h 
=> h.TouchIron(null)).Constraints(Is.Matching<Iron>(i => i.IsHot)).Throw(new BurnException());
            mouth.Expect(m 
=> m.Yell());

            var brain 
= new Brain(hand, mouth);
            brain.TouchIron(
new Iron { IsHot = true });

            mouth.VerifyAllExpectations();
        }
    }
2、Moq  V4.0.10827(2010-8-27)
  Moq是一个比较新的开源项目,最早提出了用Lambda表达式和泛型以及扩展方法实现调用API的强类型化,不用通过字符串方式描述Mock对象的方法以及属性,这个特点已经被其他Mocking Framework迅速吸收;

  Moq的语法及其简单,学习成本较低,也是最早提出隐性Record/Reply模式的Mocking Framework.

  Moq跟Rhino Mocks一样是采用Castle DynamicProxy方式实现Mock对象的构建,因此也无法对sealed类、非Virtual, 非抽象以及静态方法进行mock操作。

  被Mvccontrib项目所支持,可以被该项目的TestHelper调用,并用于ASP.NET中Stub对象的模拟。  

  轻量级应用,不需要被安装,只需要在测试项目中进行引用即可实现对其的调用。
.NET Mocking Framework对比第3张.NET Mocking Framework对比第4张代码
    [TestFixture]
    
public class BrainTests
    {
        
/// <summary>
        
/// Verify that if hand throws an exception having touched a hot iron,  <see cref="IMouth.Yell"/> gets called.
        
/// </summary>
        
/// <remarks>
        
/// Moq can mock both interfaces and classes - however, only virtual methods 
        
/// of a class can be mocked (try changing IHand/IMouth to Hand/Mouth).
        
/// </remarks>
        [Test]
        
public void TouchHotIron_Yell()
        {
            var hand 
= new Mock<IHand>();
            var mouth 
= new Mock<IMouth>();
            hand.Setup(x 
=> x.TouchIron(HotIron)).Throws(new BurnException());

            var brain 
= new Brain(hand.Object, mouth.Object);
            brain.TouchIron(
new Iron { IsHot = true });

            mouth.Verify(x 
=> x.Yell());    
        }

        
/// <summary>
        
/// Parameter expectations tend to be quite verbose in Moq, so we provide a custom matcher.
        
/// This needs a matcher method and a bool sibling method for evaluating the expectations.
        
/// Calling this matcher is technically equivalent to <code>It.Is{Iron}(i => i.IsHot)</code>.
        
/// </summary>
        public Iron HotIron
        {
            
get { return Match<Iron>.Create(x => x.IsHot); }
        }
    }

  3、Typemock 2010 商业软件,有试用版

  Typemock的实现方式比较特殊,它通过调用.NET Profiler接口,在代码运行时进行拦截,注入重定向代码,从而实现Mock处理。

  因为实现方式上的截然不同,也给予了Typemock更为强大的能力:可以Mock私有类,Sealed类以及非Virtual、非抽象甚至静态方法。

  也可以对mscorlib里定义的基本类型进行Mock,例如DateTime.Now。

  在使用API接口上,Typemock基本与Moq很类似,也是非常简单易用。

  使用Ivonna实现对ASP.NET环境下Stub对象的模拟。

  必须安装后集成到Visual Studio开发环境中才能使用,并且因为实现机制的原因,执行速度较慢。

.NET Mocking Framework对比第5张.NET Mocking Framework对比第6张代码
    [TestFixture]
    
public class BrainTests
    {
        
/// <summary>
        
/// Can mock both classes and interfaces, can mock private/static classes etc.
        
/// </summary>
        [Test, Isolated]
        
public void TouchHotIron_Yell()
        {
            var hand 
= Isolate.Fake.Instance<Hand>();
            var mouth 
= Isolate.Fake.Instance<Mouth>();
            var iron 
= new Iron { IsHot = true };
            Isolate.WhenCalled(() 
=> hand.TouchIron(iron)).WillThrow(new BurnException());

            var brain 
= new Brain(hand, mouth);
            brain.TouchIron(iron);

            Isolate.Verify.WasCalledWithAnyArguments(() 
=> mouth.Yell());
        }

        
/// <summary>
        
/// Can mock objects WITHOUT DEPENDENCY INJECTION.
        
/// </summary>
        [Test, Isolated]
        
public void TouchHotIron_Yell_NoDependencyInjection()
        {
            var hand 
= Isolate.Fake.Instance<Hand>();
            var mouth 
= Isolate.Fake.Instance<Mouth>();
            Isolate.Swap.NextInstance
<Hand>().With(hand);
            Isolate.Swap.NextInstance
<Mouth>().With(mouth);
            var iron 
= new Iron { IsHot = true };
            Isolate.WhenCalled(() 
=> hand.TouchIron(iron)).WillThrow(new BurnException());

            
//notice we're not passing the mocked objects in.
            var brain = new Brain();
            brain.TouchIron(iron);

            Isolate.Verify.WasCalledWithAnyArguments(() 
=> mouth.Yell());
        }
    }


  4、Moles V0.94.51006.1(2010-10-12)

   微软Research项目中的一员,非开源但免费使用,目前还未能发布正式版,在稳定性上稍微欠缺。
实现方式也是非常特殊:通过MSBuild的API,在编码编译时甚至编译后对Assembly进行重写,从而在已有的IL代码中插入重定向代码,从而实现Mock处理。

   Moles和Typemock一样,也是基本可以Mock所有的类和方法,甚至私有类,私有方法,也可以对mscorlib里定义的基本类型进行Mock。

   被处理过后的Assembly中直接生成了一些相应的Mock对象,因此Moles的API完全和其他Mocking Framework截然不同,而是采用约定的命名空间和首字母方式进行调用,给人感觉API比较怪异。

   Moles需要安装并集成到Visual Studio中,并且在安装过程中对.NET Framework中很多类库也进行了特殊处理,进而也可以引用相应的类库和方法实现对ASP.NET环境的支持。

二、横向比较


Rhino Mocks 3.6

Moq 4.0.10827

Typemock 2010

Moles 0.94.51006.1


实现方式

Castle Dynamic Proxy

Castle Dynamic Proxy

.NET Profiler

MS Build


授权情况

开源/免费

开源/免费

不开源/不免费

不开源/免费


强类型支持


递归Mocks对象

支持

支持

支持

支持


Partial Mocks

支持

支持

支持

支持


Virtual Method

支持

支持

支持

支持


Abstract Method

支持

支持

支持

支持


Public Class

支持

支持

支持

支持


Interface

支持

支持

支持

支持


Sealed Class 

不支持

不支持

支持

支持


Non-Abstract Method

不支持

不支持

支持

支持


Non-Virtual Method

不支持

不支持

支持

支持


Static Method

不支持

不支持

支持

支持


语法结构

一般

简炼

简炼

较差


对依赖注入框架的依赖性

依赖

依赖

不依赖

不依赖


对ASP.NET支持

MVC Contrib

MVC Contrib

Ivonna

自带


三、总结

  根据上面的概述和列表比较可以比较明显的看出:

      作为商业软件的Typemock确实除了在执行效率和安装使用方面存在一定的缺点外,基本是最为强大的Mocking Framework;

      而再看开源的Rhino Mocks和Moq基本非常接近,Moq在语法上更简炼易用一些(PS, 据说RM 4.0会大幅改进API),一般情况下这两个开源的Mocking Framework都应该可以满足需求,但他们都高度依赖于依赖注入框架,例如Unity, Autofac等等。不过关于这点,是优势还是劣势社区里也众说纷纭。不少支持者认为,正因为这样,强迫项目必须实现依赖倒置,从而降低了项目的耦合性,以达到较高的可测试性以及可维护性。而Typemock反而因为功能太过强大,无需依靠IOC容器反而受到开源社区的一些批评。

      最后再来看新秀Moles,这个东西从语法以及设计上都算是个另类,基本和其他框架没有相同之处,但功能上却也非常强大。不过从接口设计方面看,确实比较糟糕,不够优雅。另外,Moles有一个比较致命的弱点:因为Moles基于对程序集的编译后静态织入,因此,若被单元测试的程序集发生改变之后,必须整个重新运行编译再运行单元测试,这个在实际使用中是一个非常麻烦的事情。

      因此,在我看来,在项目实践中,若采用Typemock这个商业框架,则依然可以强制要求项目依赖于Ioc容器进行开发,保证不滥用Typemock的强大功能,只在不得不使用Typemock对mscorlib以及静态,私有等特殊方法进行mock时才使用。

      若采用免费方案,也许应该采用Moq为主,Moles为辅的方式进行:即使用Moq进行大部分的Mock操作,只有遇到当Moq无法解决的场景再使用Moles进行处理。

 注:文章中示例代码详见:mocking-frameworks-compare,该项目有详细的代码对比

免责声明:文章转载自《.NET Mocking Framework对比》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇数据挖掘:基于Spark+HanLP实现影视评论关键词抽取(1)ReactNative学习-滑动查看图片第三方组件react-native-swiper下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

spring boot + vue + element-ui全栈开发入门——项目部署

 前言 常用的部署方式有两种: 1.是把生成好的静态页面放到spring boot的static目录下,与打包后的spring boot项目一起发布,当spring boot运行起来后,自然而然就能访问到静态页面文件了。 这种方法比较简单,适用于非常小型的系统。优点是:不需要复杂的配置。而缺点也很明显:需要两者一同发布。我在这里就不做赘述了。 2.是通过...

Android Studio安装及其使用

为了不与Java开发混用,所以不使用IDEA开发安卓应用而选择Android Studio。 官网下载安装 Android Studio(https://developer.android.google.cn/studio/) 启动时会提示 Unable to access Android SDK add-on list,点 cancel,然后就会弹出An...

【Bullet引擎】刚体类 —— btRigidBody

btRigidBody类主要用于刚体数据的计算。 在模拟刚体动画过程中,可以使用btRigidBody类获取所保存的刚体对象,进而控制刚体对象的旋转和位移。进行刚体模拟计算需要经常用到此类。 API:http://bulletphysics.org/Bullet/BulletFull/classbtRigidBody.html 创建刚体对象 btC...

Android数据库ORMlite框架04

2.10 索引成员 在你的数据类中ORMLite提供了一些多种成员索引有限的支持。首先,它重点指明任何已经被标记成id的成员变量已经被编入索引。一个id成员变量不需要添加额外构建的索引并且如果他们被指定的话那么数据库会产生错误。 添加一个索引到没有id的成员变量,你需要添加index = true布尔域到@DatabaseField注解。这将会在表被创建时...

springboot配置idea 热部署

背景: 在开发中,当我们修改代码之后,每次都要重新启动,很是浪费时间,在springboot中就有一种热部署方式,可以实现想要修改不需要每次都重新启动,保存即可生效 用法: 一、maven 添加   二、添加编译时候的配置 如下的配置在idea的springBoot项目中可以不需要添加,因为springBoot项目中已经默认有了此配置(亲测没有...

DOM,javascript,Web API之间的关系——onclick 引起的思考与调研

平时习惯了用js操作dom树来与html页面进行交互,基本都是通过web API接口实现的,最近看闭包和原生js的知识点比较多,昨天无意中看到了onclick中的this指向问题,遂用native js方式模拟了onclick再html标签里的this和js事件里的this,详见上一篇博客:用js的eval函数模拟Web API中的onclick事件 下文...