• Molas——.NET依赖分离框架


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

     

    下载和安装

    下载Moles后直接安装就可以了,里面集成有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 instrumented
    To 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环境。

    参考资料

    http://research.microsoft.com/en-us/projects/pex/molesmanual.pdf http://research.microsoft.com/en-us/projects/pex/documentation.aspx

  • 相关阅读:
    CodeForces
    设计模式之装饰模式和代理模式区别与联系
    java反射 概念
    Java 反射详解 转载
    Spring--AOP 例子
    MD5加密
    面向对象编程思想(OOP)
    软件测试assert
    junit4.9测试用例 spring测试用例 Assert 注解
    断言
  • 原文地址:https://www.cnblogs.com/vento/p/2858151.html
Copyright © 2020-2023  润新知