博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Molas——.NET依赖分离框架
阅读量:5059 次
发布时间:2019-06-12

本文共 6257 字,大约阅读时间需要 20 分钟。

是由微软研究院()开发的.NET依赖分离框架,它实现了使用自定义的委托(delegate)方法来替换原有类中的方法,以达到分离依赖,方便单元测试的目的。Moles在功能和用法上与开源的IoC框架Moq很像,但Moles有一些Moq实现不了功能,如替换静态方法,去掉静态构造函数,突破访问限制等。Molas非常有利于对ASP.NET WebForm构建的网站和依赖第三方类库的程序进行单元测试。

 

下载和安装

后直接安装就可以了,里面集成有VS2010的插件,安装成功后,VS2010右键菜单中会集成Moles功能菜单。

使用示例

我们试下测试2000年千年虫的bug。在VS2010中创建一个MoleDomain的类项目,并创建类Y2KChecker,代码如下:

namespace MoleDomain{    public static class Y2KChecker    {        public static void Check()        {            if (DateTime.Now == new DateTime(2000, 1, 1))                throw new ApplicationException("y2kbug!");        }    }}

现在我们要测试这段代码,确定当时间为2000/1/1时,程序能正确抛出异常。很显然这段代码没法做单元测试,因为代码中的DateTime.Now是依赖系统时钟的,只返回当前时间,我们没法改变它的值使它刚好等于2000/1/1。怎么办好呢? 使用Molas解决这个问题很简单。创建一个测试项目,并引用MoleDomain项目,单元测试代码如下:

[TestMethod][ExpectedException(typeof(ApplicationException))]public void Test(){    Y2KChecker.Check();}

运行测试,会显示预期的未通过,因为DateTime.Now现在返回的还是系统时间。

我们试下使用Molas替换DateTime.Now的返回值,在测试项目引用列表中,右键MoleDomain,选择“Add Moles Assembly”,确定后会自动在项目中增加一个MoleDomain.moles文件,moles后缀的文件是让Moles对该程序集自动生成对应的Molas类型程序集,以便测试时使用。

右键测试项目,选择“重新生成”,会发现程序自动引用了很多Moles相关的程序集,如Microsoft.Moles.Framework,还有自动生成的 MolaDomain.Moles程序集。

要使Moles正常运行,需要改下原来的单元测试代码。在测试方法上方加上HostType特性,并写下替换DateTime.Now返回值的代码:

[TestMethod][ExpectedException(typeof(ApplicationException))][HostType("Moles")]public void Test(){    //利用委托替换原来的返回值    MDateTime.NowGet = () => new DateTime(2000, 1, 1);    Y2KChecker.Check();}

再次运行测试,发现还是失败,提示错误:

测试方法 MoleDomain.Test.Y2KCheckerTest.Test 引发了异常 Microsoft.Moles.Framework.Moles.MoleNotInstrumentedException,但应为异常 System.ApplicationException。异常消息: Microsoft.Moles.Framework.Moles.MoleNotInstrumentedException: The System.DateTime System.DateTime.get_Now() was not instrumentedTo resolve this issue, add the following attribute in the test project:using Microsoft.Moles.Framework;[assembly: MoledType(typeof(System.DateTime))]

提示缺少一些引用配置,在测试命名空间上方加上代码:

using Microsoft.Moles.Framework;[assembly: MoledType(typeof(System.DateTime))]namespace MoleDomain.Test{    .....}

再次运行测试,终于通过测试了:)

Mole基础知识

原始类成员方法对应的Mole类型属性如下:

  • ◇ 静态方法表示为mole类型的静态属性
  • ◇ 类实例方法表示为嵌套的AllInstances类型的静态属性
  • ◇ 类构造函数表示为mole类型的命名为Constructor的静态属性

下面部分说明下如何使用. Static Methods 为mole类型的静态属性附加委托方法可以替换类静态方法的内容。mole类型属性只能附加一个委托方法。如MyClass类有一个静态方法MyMethod

public static class MyClass {    public static int MyMethod() {        ...    }}

我们附加一个mole到MyMethod中,使它一直返回5:

MMyClass.MyMethod = () =>5;

自动生成的MMyClass类型的代码结构如下:

public class MMyClass {    public static Func MyMethod {        set {            ...        }    }}

安装Moles框架后,使用右键的“Add Moles Assembly”功能添加.mole后缀文件后,MMyClass就能自动生成。

实例方法(对所有实例生效)

和静态方法相似,也可以对所有实例方法进行mole。实例方法放置在嵌套类AllInstances的静态属性中,例如下面MyClass实例的MyMethod方法:

public class MyClass {    public int MyMethod() {        ...    }}

mole一个方法使所有实例对象都返回5:

MMyClass.AllInstances.MyMethod = _ => 5;

自动生成的MMyClass结构如下:

public class MMyClass : MoleBase {    public static class AllInstances {        public static FuncMyMethod {            set {                ...            }        }    }}

实例方法(对一个实例生效)

对不同的实例,实例方法可以mole不同的委托方法。mole的属性实际是mole类型实例自己的属性(不是静态方法),每个mole类型实例都会有一个原始类型的实例对象。如MyClass的实例方法MyMethod

public class MyClass {    public int MyMethod() {        ...    }}

我们可以创建两个MMyClass的实例对象,一个使它返回5,另一个使它返回10:

var myClass1 = new MMyClass(){    MyMethod = () => 5};var myClass2 = new MMyClass() { MyMethod = () => 10 };

自动生成的mole类型代码结构如下:

public class MMyClass : MoleBase {    public Func MyMethod {        set {            ...        }    }public MyClass Instance {        get {            ...        }    }}

原始类型对象可以通过mole实例对象的Instance属性获得:

var mole = new MMyClass();var instance = mole.Instance;

mole实例对象也可以隐式转换为原始类型对象,所以你可以直接赋值对原始类型,如下:

var mole = new MMyClass();MyClassinstance = mole;

构造函数(Constructors)

类构造函数也可以mole来进行一些赋值操作。类构造函数表示为mole类型的静态方法Constructor,如下面的MyClass类带有一个int参数的构造函数:

public class MyClass {    public MyClass(int value) {        this.Value = value;    }...}

通过附加构造函数使以后的所有实例的Value属性都返回-5:

MMyClass.ConstructorInt32 = (@this, value) => {    var mole = new MMyClass(@this) {        ValueGet = () => -5    };};

如果你只想mole后面一个实例,我们只需把Constructor静态属性赋null值,如:

MMyClass.ConstructorInt32 = (@this, value) => {    ...MMyClass.ConstructorInt32 = null;};

需要注意的是,每个mole类型都有两个构造函数,当需要一个新的mole实例对象时,使用默认的构造器;而带有一个原始类型参数的构造函数,只应该在mole构造函数时使用。

public MMyClass() { }public MMyClass(MyClass instance) : base(instance) { }

自动生成的MMyClass代码结构如下:

public class MMyClass : MoleBase{    public static Action ConstructorInt32 {        set {            ...        }    }    public MMyClass() { }    public MMyClass(MyClass instance) : base(instance) { }    ...}

基类成员(Base Members)

只要把子类实例作为基类构造函数的参数传入,就可以创建一个基类的mole对象,并访问到基类中的mole属性。例如,基类Base有一个MyMethod的方法,而ChildBase的子类:

public abstract class Base {    public int MyMethod() {        ...    }}public class Child : Base {}

通过创建一个MBase对象我们能设置Base的mole属性:

var child = new MChild();new MBase(child) { MyMethod = () => 5 };

注意这里,当MChild实例作为传入MBase构造函数时,会被隐式转换为Child实例。 MChild和MBase的自动生成代码如下:

public class MChild : MoleBase {    public MChild() { }    public MChild(Child child)        : base(child) { }}public class MBase : MoleBase {    public MBase(Base target) { }    public Func MyMethod    { set { ... } }}

静态构造函数

静态构造函数在Moles中被特殊对待,Moles只能简单地抹去静态构造函数,而不能重新为它附加新的委托方法。Moles通过指定[MolesEraseStaticConstructor]特性来抹去一个类的静态构造函数。

[assembly: MolesEraseStaticConstructor(typeof(MyStatic))]class MyStatic {    static MyStatic() {        throw new Exception(); // needs moling…    }}

终结器(Finalizers)

对于终结器,Moles也是特殊对待的。Moles也是只能简单抹去终结器,通过指定[MolesEraseFinalizer]特性实现。

[assembly: MolesEraseFinalizer(typeof(MyFinalizer))]class MyFinalizer {    ~MyFinalizer() {        throw new Exception(); // needs moling…    }}

私有方法

假如私有方法的签名类型是可见的,Moles会为私有方法自动生成mole属性。签名类型可见是指,参数类型或返回值类型是可见的,不是私有类型。

绑定接口

当类有实现接口时,Moles自动生成的mole类型会提供立即绑定接口成员的方法。例如,MyClass实现了s IEnumerable接口:

public class MyClass : IEnumerable {    public IEnumerator GetEnumerator() {        ...    }...}

通过mole类型的Bind方法,我们可以简捷地mole接口实现:

var myClass = new MMyClass();myClass.Bind(new int[] { 1, 2, 3 });

自动生成的MMyClass代码结构如下:

public class MMyClass : MoleBase {    public MMyClass Bind(IEnumerable target) {        ...    }}

Moles缺点

Moles缺点是,测试运行比较慢,还有测试代码只能在本机上才能测试通过,假如同伴获取代码后需要运行单元测试,必须也安装Molas环境。

参考资料

转载于:https://www.cnblogs.com/vento/archive/2012/02/15/2858151.html

你可能感兴趣的文章
【MAC下学习Unix网络编程】第一个例子中解决unp.h 在mac下的编译问题
查看>>
jquery ztree插件
查看>>
Java finally关键字
查看>>
InstallShield Limited Edition for Visual Studio 2013 图文教程(教你如何打包.NET程序)
查看>>
LUA安装过程
查看>>
Python-aiohttp百万并发
查看>>
牛顿法求平方根及习题1.6-1.8
查看>>
电赛初探(二)——语音采集回放系统
查看>>
SQL SERVER 如何调试存储过程
查看>>
php修改和增加xml结点属性
查看>>
Mysql插入数据是问号的乱码
查看>>
设计模式之原型模式
查看>>
(转)页面滚动图片加载
查看>>
使用Carthage安装第三方Swift库
查看>>
修改mysql root的密码
查看>>
LeetCode 53. Maximum Subarray
查看>>
LeetCode 151. Reverse Words in a String
查看>>
LeetCode Reverse Bits
查看>>
LeetCode The Skyline Problem
查看>>
干货!一篇文章集合所有Linux基础命令,适合所有菜鸟学习和老手回顾!
查看>>