zoukankan      html  css  js  c++  java
  • Unit Tests Tool

    From: http://www.cnblogs.com/wJiang/archive/2010/02/21/1670632.html


    Moq即Mock You Framework,故名思意是一个类似Mockery,JMock的Mock框架。 是google上的一个.net开源项目
    项目扉页 相关下载
    http://code.google.com/p/moq/ http://code.google.com/p/moq/downloads/list

    先说说一般的使用Mock进行测试的基本过程:

    image

    Moq的3.x版本已经不同以往,这得益于.net3.x为大家带来的Lambda表达式,Linq。

    说说Moq的优点:

    a.完全开源。开源的好处我就不多说了,不过相比java社区上的活跃,Apache项目的浩繁,.net在这方面确实逊色不少。

    b.简单易学,便于使用。Moq的设计原则就是“以极低的门槛获取良好的重构能力”在我个人看来,Moq是我用过的上手最容易使用起来最自然的Mock。

    Moq中几个重要的类(在后续文章中详细介绍):

    Mock<T>:通过这个类我们能够得到一个Mock<T>对象,T可以是接口 和 类。它有一个公开的Object属性,这个就是我们Moq为我们模拟出的对象。

    It:这是一个静态类,用于过滤参数。

    MockBehavior:用于配置MockObject的行为,比如是否自动mock。

    MockFactory:Mock对象工厂,能够批量生产统一自定义配置的Mock对象,也能批量的进行Mock对象测试。

    Match<T>:如果你先嫌It不够用就用Match<T>,通过它能够完全自定义规则。

    初识Moq

    新建一个测试,我们用三行代码演示一个Moq的使用。

    [TestMethod()]
    [Owner(wJiang)]
    public void MoqTest0()
            {
                //make a Mock Object by Moq
                var mo = new Mock<TargetInterfaceOne>(); 
    
                //Setup our Mock Object
                mo.Setup(p => p.MethodWithParamAndResult("abc")).Returns("123"); 
    
                //Assert it!
                Assert.AreEqual("123", mo.Object.MethodWithParamAndResult("abc"));
            }
    

    说明:

    new Mock<T>返回一个Mock对象,我们可以用var接收,这样写起来更方便些,Mock<T>有一个Object属性,存储的就是我们的模拟对象实例。

    Setup的参数是一个Lambda Expression,我们可以理解为:“当 被mock的对象p调用MethodWithParamAndResult方法 并且参数为”abc”的时候”。后面再加一个Return(“123”)我们可以理解为:(在之前Setup的情况下)返回的值为”123”。这样,我们就填充好了一个“伪对象”的行为,我们只让它做一件实事儿:当我们调用mo.Object.MethodWithParaAndResult方法并且参数为”abc”时会返回”123”。

    实际上我们不仅能够在Setup后面接Returns方法还能接诸如Throws、Verify之类的方法,这是为什么呢?Setup方法会返回一个ISetup对象,看看ISetup的定义:

    public interface ISetup<TMock, TResult> : ICallback<TMock, TResult>, IReturnsThrows<TMock, TResult>, IReturns<TMock, TResult>, IThrows, INever, IVerifies, IHideObjectMembers where TMock : class

    恩,是链式编程,ISetup接口继承了很多接口,这里我们注意到IReturns<TMock,TResult>,看看IReturns<TMock, TResult>定义:

    public interface IReturns<TMock, TResult> : IHideObjectMembers where TMock : class。

    里面有一个方法:IReturnsResult<TMock> Returns<T>(Func<T, TResult> valueFunction);

    所以我们还能写出这样的代码:

    mo.Setup(p => p.MethodWithParamAndResult("abc"))

    .Returns("123")

    .Callback(……)

    .Throws(……)

    .Verifiable(……);

    呵呵,这种代码理解起来是很自然的。Moq设计的是不是很人性化呢。


    上一篇介绍了Moq并给出了一个入门的例子。下面说说Moq中的参数匹配。先看Mock<T>的一个方法。

    public ISetup<T> Setup(Expression<Action<T>> expression);

    熟悉.NET框架尤其是开发过基于MVVM的WPF应用程序的朋友对Action<T>和Prediect<T>这两个泛型委托应该不陌生,这两个委托的含义很简明,前者表示给定一个参数然后施展一个行为,后者表示施行行为的前提。

    假如我们有一个接口IA,IA有一个方法签名string MethodA1(stringidentity)。那么我们怎么模拟一个实现IA接口的对象MethodA1呢?

    var mo = new Mock<IA>();
    mo.Setup( p => p.MethodA1(“50”)).Return(“Hello, mocker”);
    

    这个例子和上一篇是一样的,这里在额外说明下:Return的参数类型取决于方法的返回类型,如果我们把MethodA1返回void,那么就没法Return方法了,这里由于返回string类型,所以能Return。

    当然,方法参数可可选值很多,"51",”52”,”53”,”54”,”55”,”56”……如果我们一个一个的Setup怎么能行?我向大家隆重推出两个类:It,Match<T>。

    先说It,It很适合用来匹配数字,字符串参数,它提供了如下几个静态方法(取自Moq的官方API文档):

    image

    第一个方法的参数Expression<Predict<TValue>>类型,当你需要某种类型并且这种类型要通过代码来判断的话可以使用它。

    第二个方法没有参数,只要是TValue类型的就能匹配成功。

    第三个方法用来匹配两个的TValue类型值之间的参数。(Range参数可以设定开闭区间)

    第四个是用正则表达式匹配。(仅限于字符串类型参数)

    举个例子:

    [TestMethod]
    [Owner(wJiang)]
    public void MoqTest1()
    {
                var mo = new Mock<TargetInterfaceOne>();
                mo.Setup(p => p.MethodWithParamAndResult(It.IsRegex("^God.*$"))).Returns("bless me");
                mo.Setup(p => p.MethodWithParamAndResult(It.Is<string>((param => param.IndexOf("Evil") >= 0)))).Returns("away from me");
                //mo.Setup(p => p.MethodWithParamAndResult(It.):
                Assert.AreEqual("bless me", mo.Object.MethodWithParamAndResult("God comes"));
                Assert.AreEqual("away from me", mo.Object.MethodWithParamAndResult("Evil is here"));
            }
    

    但是It提供的功能还是显得有些弱,这时候我们可以自定义匹配验证规则。这就用到了Match<T>。

    Match<T>是个静态类,它值公开了一个静态方法(重载了两个版本):public static T Create(Predict<T> condition, ……)。

    先看下下面的代码便于讲解,我们写个用于参数匹配的静态帮助类。

    [TestMethod()]
            public void MoqTestA()
            {
                var mo = new Mock<TargetInterfaceOne>();
                mo.Setup(p => p.MethodWithParamAndResult(MatchHelper.CustomMatcher("abc"))).Returns("123"); 
    
                Assert.AreEqual(mo.Object.MethodWithParamAndResult("abc"), “123);
    
                Assert.IsNull(mo.Object.MethodWithParamAndResult(“newyorktimesbyflex”));
            }
    
    public static class MatchHelper
            {
                public static string CustomMatcher(string arg)
                {
                    return Match<string>.Create( 
                        p => p.Equals(arg), ()=>null);
                } 
    
                public static IEnumerable<string> Contains(string key)
                {
                    return Match<IEnumerable<string>>.Create(p=>p.Contains(key));
                }
            }
    

     对我们而言CustomMatcher(string arg)和Contains(string key)就是验证方法。和上个例子比较,就是将p => p.MethodWithParamAndResult(……)里面的东西换成了我们自己的方法。另外一个Contains方法是可用来满足这样一个需求:给定的参数为可迭代类型,只有包含特定的元素时才能匹配


    Raise

    如果你说会用Setup,那么Raise就更简单了。这里注意下它是无返回值类型。

    mockView.Raise(v => v.SelectionChanged += null, new OrderEventArgs { Order = new Order("moq", 500) });

    Callback

    Callback嘛,顾名思义就是回调。使用Callback可以使我们在某个使用特定参数匹配的方法在被调用时得到通知。比如我们要得知在一次测试中某个方法被调用了几次,可以这么做:

    [TestMethod]
            public void MoqTest2()
            {
                var mo = new Mock<TargetInterfaceOne>();
                int counter = 0;
                mo.Setup(p => p.MethodPure()).Callback( () => counter++ ); 
    
                mo.Object.MethodPure();
                mo.Object.MethodPure(); 
    
                Assert.AreEqual(2, counter);
            }
    

    在这段代码中我们在Setup方法后接了个Callback方法(或者说是调用了ISetup的Callabck方法实现)。这段代码的意思就是在调用MethodPure方法时会执行Callback中的Action委托。

    调用两次MethodPure(),测试结果证明确实累加了两次counter。

    Verify

    有些时候我们并不关注方法的返回结果,而是关注某个方法是不是在内部被调用。

    这时我们就用到了Verify/VerifyAll。同时有个有用的类型Times,规定应该调用多少次。如果验证失败则抛出异常。

    [TestMethod()] 
    
    public void MoqTest3()
    {
        var mo = new Mock<TargetInterfaceOne>();
        mo.Setup( p => p.MethodPure() );
        mo.Setup( p => p.MethodWithParam("123")).Verifiable("it should be invoked");
        //mo.Object.MethodPure();
        mo.Object.MethodWithParam("123");
        mo.Verify( p => p.MethodPure(), Times.AtLeastOnce() );
        mo.Verify(p => p.MethodWithParam("thto"), Times.AtLeastOnce(), 
            "this method  invoking of MethodWithParam() with the parameter: "thto" is not happened"); 
    
        mo.Object.MethodPure();
    }
    

    如果在MethodPure前调用mo.Verify(p => p.MethodPure())则会抛出异常,因为不符合条件:在执行verify前至少调用一次。

    关于Verify和VerifyAll

    这两个方法会对Mock对象的所有Setup过的方法进行验证,那么有什么不同呢?注意到上面代码中绿色字体部分,有一个Verifiable方法,可以理解为为这个Setup的东西加了个验证标记。而Setup(p=>p.MethodPure())时就没有些,那么我们在使用调用Verify()时只会对MethodWithParam(“123”)进行验证而不会对MethodPure()是否被调用过进行验证。

  • 相关阅读:
    eslint 的 env 配置是干嘛使的?
    cookie httpOnly 打勾
    如何定制 antd 的样式(theme)
    剑指 Offer 66. 构建乘积数组
    剑指 Offer 65. 不用加减乘除做加法
    剑指 Offer 62. 圆圈中最后剩下的数字
    剑指 Offer 61. 扑克牌中的顺子
    剑指 Offer 59
    剑指 Offer 58
    剑指 Offer 58
  • 原文地址:https://www.cnblogs.com/wushuaiyi/p/4671446.html
Copyright © 2011-2022 走看看