推荐一个单元测试模拟框架:Nsubstitute

 

推荐单元测试新的模拟框架:Nsubstitute

          

     目前,.NET已经有很多强大模拟框架,为什么还要再重新写一个呢?按照Nsubstitute的官方说法是:所有的模拟框架都已经有强大的功能,但是现存的框架当中,没有一个满足我们对更简洁语法风格的追求。   

     第一次看到Nsubstitute,是在看Nunit的源码时发现,实际上Nunit已经不推荐大家使用它原来的Mock框架,它引入了Nsubstitute。经了解,Nsubstitute已经是一个具有两年多历史的模拟框架,也算是比较年轻的框架,它昨天(2012-5-4)刚发布了1.4版本。模拟框架很多,我们的选择很多,但是从现在开始,我们可以考虑一下用Nsubstitute,多了一个选择。

1. Nsubstitute简介

   它是一个开源的框架,源码是C#实现的。你可以在这里获得它的源码:https://github.com/nsubstitute/NSubstitute 

   NSubstitute 更注重替代(Substitute)概念。它的设计目标是提供一个优秀的测试替代.NET模拟框架。它是一个模拟测试框架,简洁的语法,使得我们能够把更多的注意力放在测试工作减轻我们的测试配置工作,以满足我们的测试需求,帮助完成测试工作它提供最经常需要使用测试功能易于使用,语句更符合自然语言,可读性更高对于单元测试的新手或专注于测试的开发人员具有简单、友好的语法,使用更少的lambda表达式编写完美的测试程序

 

      NSubstitute 采用的是Arrange-Act-Assert测试模式,你只需要告诉它应该如何工作,然后断言你所期望接收到请求,就大功告成了。因为你有更重要的代码编写,而不是去考虑是需要一个Mock还是一个Stub

    对比MoqNSubstitute 的语法比更简练。这里的主要目的并不是为了比较框架的优劣。

2. 如何获取Nsubstitute

有两种获得Nsubstitute方式:

一是通过NuGet或者OpenWrap工具在Vs2010上安装Nsubstitute

二是通过下载Nsubstitute 组件(https://github.com/nsubstitute/nsubstitute/downloads ),然后在测试项目当中引用 NSubstitute.dll 文件。现在的最新版本是1.4

3. 测试方法

 可以创建接口的实例,并设定接口方法的返回值,供测试。

1) 定义一个基本的计算器接口

public interface ICalculator

{

    int Add(int a, int b);

    int Subtract(int a, int b);

}

2) 创建测试项目、及测试程序

3) 引用命名空间声明

首先,C#测试源码文件添加命名空间引用

using NSubstitute

4) 创建接口实例

calculator = Substitute.For<ICalculator>();

5) 设定替代对象的接口方法返回值

     指定Add被调用时,传入参数值分别为12时,替代返回值3

calculator.Add(1, 2).Returns(3);

6) 断言

 Assert.AreEqual(calculator.Add(1, 2)3);

7) Received的使用

另外,我们还可以检验替代对象的指定场景是否被调用,而哪些场景没有被调用,例如:

calculator.Add(1, 2);

calculator.Received().Add(1, 2);

calculator.DidNotReceive().Add(5, 7);

如果场景在之前没有被调用,Received() 断言则会失败

例如:calculator.Received().Add(2, 3);

我们将会发现测试异常的提示信息如下:

    测试方法 Calculator.Tests.ICalculatorTest.AddTest 引发了异常

NSubstitute.Exceptions.ReceivedCallsException: Expected to receive a call matching:

Add(2, 3)

Actually received no matching calls.

Received 1 non-matching call (non-matching arguments indicated with '*' characters):

Add(*1*, *2*)

NSubstitute支持设置参数返回值,并断言已经被调用。下面是更复杂的使用:

calculator.Add(10, -5);

calculator.Received().Add(10, Arg.Any<int>());//第二个参数的值为任一int类型的整数。

calculator.Received().Add(10, Arg.Is<int>(x => x < 0));//第二个参数的值必须是小于零,否则将报异常

我们可以使用替代对象的参数匹配及通过Returns()方法得到一些更多的行为:

//定义第一个参数及第二个参数为任意整数,并且返回值为两个参数之和。

calculator.Add(Arg.Any<int>(), Arg.Any<int>()).Returns(x => (int)x[0] + (int)x[1]); 

Assert.AreEqual(calculator.Add(5, 10), 15);  

4. 测试对象属性及事件委托

1) 定义一个接口

public interface IPerson

    {

        string Name { get; set; }

        event EventHandler Sleep;

    }

2) 创建测试项目、及测试程序

3) 引用NSubstitute.dll,并声明命名空间

首先,C#测试源码文件添加命名空间引用

using NSubstitute

4) 创建接口实例

target= Substitute.For<IPerson>();

5) 测试属性的读写操作

           target.Name.Returns("小明");

            //测试属性的读操作

            Assert.AreEqual(target.Name, "小明");

            //对属性进行写操作

            target.Name = "王二";

            Assert.AreEqual(target.Name, "王二");

还可以使用Returns()设置的多个返回值序列

           target.Name.Returns("A", "B", "C");

            Assert.AreEqual(target.Name, "A");

            Assert.AreEqual(target.Name, "B");

            Assert.AreEqual(target.Name, "C");

 

6) 测试事件

            bool eventWasRaised = false;

            //定义委托事件

            target.Sleep += (sender, args) => eventWasRaised = true;

            //触发事件

            target.Sleep += Raise.Event();

            //断言事件被触发

            Assert.IsTrue(eventWasRaised);

posted @ 2012-05-05 14:49  可乐加冰  阅读(3046)  评论(0编辑  收藏  举报