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

上一篇 / 下一篇  2012-05-08 09:24:10 / 个人分类:杂谈

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

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

l1[l6a"Mh^;a'^I051Testing软件测试网G#QVX@t$KFPT

  1、Nsubstitute简介

&I:_G~9X)@0

ao tf/e8NN6w~0  它是一个开源的框架,源码是C#实现的。你可以在这里获得它的源码:https://github.com/nsubstitute/NSubstitute51Testing软件测试网cN y [7E

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

S(Y I'T;i]X051Testing软件测试网`k2C*O*~

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

6BB2d0jAEE0

,o] na d+^ zF0  对比Moq,NSubstitute 的语法比更简练。这里的主要目的并不是为了比较框架的优劣。51Testing软件测试网-jR!V&\ _/`6c

51Testing软件测试网 D#rW0lt7N[

  2、如何获取Nsubstitute?51Testing软件测试网_ N4Z)@r(G.?HZ

.H~3r,{6M4gF0  有两种获得Nsubstitute的方式:51Testing软件测试网MCy2V ^ QA

51Testing软件测试网 Sxqo})s

  一是通过NuGet或者OpenWrap工具在Vs2010上安装Nsubstitute。51Testing软件测试网&p%n} Sl#t \t5b

*`] zP.k#d2s&w;j0  二是通过下载Nsubstitute 组件(https://github.com/nsubstitute/nsubstitute/downloads ),然后在测试项目当中引用 NSubstitute.dll 文件。现在的最新版本是1.4。51Testing软件测试网!X kiS(B

U9KKcw"vl,P'Z0  3、测试方法

&~)t4_0]D[8eWo'D$J0

B@)l*g-fxad4c l Z0  可以创建接口的实例,并设定接口方法的返回值,供测试。51Testing软件测试网)ms+_'Kj x

.T+O%O8|J fr G0  1)定义一个最基本的计算器接口:

-Wn*TO*R4RK0
public interface ICalculator51Testing软件测试网IX[ii
{
KuS`0lFo0    int Add(int a, int b);
A8n6y\;Mb"m0    int Subtract(int a, int b);
](K%Eprax0}

_7~G)T}s0  2)创建测试项目、及测试程序51Testing软件测试网;El Tzi,k3X:D

51Testing软件测试网#^(S^9Y,Pj

  3)引用命名空间声明

W-lI#mr4?"{!_|*V051Testing软件测试网#mZ,\!z6~!U{.~4J

  首先,在C#测试源码文件添加命名空间引用51Testing软件测试网)sK"G^e*nL)uP

51Testing软件测试网LI%W Z0A0n0[ kT

  using NSubstitute。51Testing软件测试网7H'j1F#y4d Bn

51Testing软件测试网H0i@5R`#flR v;W

  4)创建接口实例51Testing软件测试网l%k%?$T y/yd

-{L4T#]3Yk_+y0  calculator = Substitute.For<ICalculator>();51Testing软件测试网 YND%c${#p

yw O-SU+`$O i0  5)设定替代对象的接口方法返回值

Rr ]U3yYG U y(n0

-^.G/rYlS Uz0  指定Add被调用时,传入参数值分别为1、2时,替代的返回值为3

4u#d/ag"F&K5V j7`8L0

&{| Q2K!z0  calculator.Add(1, 2).Returns(3);

~OMh@F8] Y;lB051Testing软件测试网].M'`pX9Q I(AB

  6)断言

+c-F)O:[-_(Svt051Testing软件测试网m g7{/VxPvi jn

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

m*m] N2Wb+Z h0

C ?t#K@#yL07)Received的使用51Testing软件测试网w&X \s'q

51Testing软件测试网9m|7O$PWIJV

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

9W,v4D)F8[.s0

-`1t[:c3me8T0  calculator.Add(1, 2);
i&f3KT qe9y0  calculator.Received().Add(1, 2);51Testing软件测试网5|'R:X4g*o/u
  calculator.DidNotReceive().Add(5, 7);
51Testing软件测试网zv*\(LX)RO y+~

2j2~ o6dg9RS^0  如果场景在之前没有被调用,Received() 断言则会失败。51Testing软件测试网a3?s'}1tSb7P

51Testing软件测试网 kka2_F3M

  例如:calculator.Received().Add(2, 3);51Testing软件测试网u\n l s'|

ktW4S7K$[)_^;m0  我们将会发现测试异常的提示信息如下:51Testing软件测试网N/Z9izj;c7vY

#C]y s.A4DV.m(K051Testing软件测试网't-B3M&U:{!cO%L0}

测试方法 Calculator.Tests.ICalculatorTest.AddTest 引发了异常:
.yd.D(MzW+C0NSubstitute.Exceptions.ReceivedCallsException: Expected to receive a call matching:
1W ~Tx8s3k0Add(2, 3)
6k8xZ^5J2Nd0Actually received no matching calls.
Q'N!@#o&ie"XD0Received 1 non-matching call (non-matching arguments indicated with '*' characters):51Testing软件测试网b6h]M0g~Fw C
Add(*1*, *2*)

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

+P`sy6K]0

g[KaqMJ8jwG,P0

QH}1K,m6b0
calculator.Add(10, -5);
;iB!R?4R0calculator.Received().Add(10, Arg.Any<int>());//第二个参数的值为任一int类型的整数。51Testing软件测试网a B3s,Q|%R|,\"g
calculator.Received().Add(10, Arg.Is<int>(x => x < 0));//第二个参数的值必须是小于零,否则将报异常

i hz${u4y;i0  我们还可以使用替代对象的参数匹配及通过Returns()方法得到一些更多的行为:51Testing软件测试网R-T'LaI ?3t

51Testing软件测试网(g3_?$iP*a^&Z:m

51Testing软件测试网@/[N6G Bq9`gg

//定义第一个参数及第二个参数为任意整数,并且返回值为两个参数之和。51Testing软件测试网Aw${"d2yi/y
calculator.Add(Arg.Any<int>(), Arg.Any<int>()).Returns(x => (int)x[0] + (int)x[1]);
xt4e9f;q?0Assert.AreEqual(calculator.Add(5, 10), 15);
51Testing软件测试网 Z Pep1o5p(F

  4、测试对象属性及事件委托51Testing软件测试网]~[ kH VuHxpHU

51Testing软件测试网&cix'K1]]$G-`#K

  1)定义一个接口:

_${:Rnhg051Testing软件测试网IRe T-N aS-S

51Testing软件测试网O|&k,V.egO

public interface IPerson
}:_9amS:Fa0    {51Testing软件测试网U7qNH#UT:I9b5D'U ^3E
        string Name { get; set; }51Testing软件测试网3Zz0K{E2U|5b
        event EventHandler Sleep;
)n2p1[*ZFo2~0    }

6A-jHUSX!F#R0  2)创建测试项目、及测试程序

5Z[H,w A.t#w051Testing软件测试网z7Otq7C!g/BL1X;T

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

mA8e:n_-`3R051Testing软件测试网@5z/n;B0f$]A(H

  首先,在C#测试源码文件添加命名空间引用51Testing软件测试网(}zoR7^5u A

51Testing软件测试网P8d.aEie~

  using NSubstitute。51Testing软件测试网5m5]-I&O!i'QT5}e.c

Wi}&v|k6p&Z0  4)创建接口实例51Testing软件测试网 _%Xac2^:O

hh [9E$BbGo0  target= Substitute.For<IPerson>();51Testing软件测试网#fP\5ii/K9aQ2c

D\2dYK4g \0Z0  5)测试属性的读写操作

t9[ o9q B2Ye051Testing软件测试网;JM0V+s,`e

51Testing软件测试网 Rd6f#o;z8["[

target.Name.Returns("小明");51Testing软件测试网 i*qbfc
//测试属性的读操作51Testing软件测试网7c I1v2U7{:NmO5O9h3@2Ng
Assert.AreEqual(target.Name, "小明");
2u[%x0{&X }0//对属性进行写操作51Testing软件测试网r4dfO)H
target.Name = "王二";51Testing软件测试网 dUnHy\B
Assert.AreEqual(target.Name, "王二");

Bw4}eQPG8m(H A0  还可以使用Returns()设置的多个返回值序列51Testing软件测试网Ayv;LlUl"?H

FVhT9f _051Testing软件测试网gK!aCmnqT5w"zh

target.Name.Returns("A", "B", "C");51Testing软件测试网u^Fp Y[
     Assert.AreEqual(target.Name, "A");
z&U:gf4e+vqt0     Assert.AreEqual(target.Name, "B");51Testing软件测试网Cc$Dn]-@X
     Assert.AreEqual(target.Name, "C");
51Testing软件测试网n+U`1{ [Z$I

  6)测试事件

tVD,Mn0A R0

!kY8d:Y~2b,~gK4r bI051Testing软件测试网Et;XE~n

bool eventWasRaised = false;51Testing软件测试网P c6\q,@.~Y9a#Q)j
//定义委托事件
g$kJ7QUxg.R0target.Sleep += (sender, args) => eventWasRaised = true;
9A#T#yZ Z0//触发事件
hZB/[r ~0target.Sleep += Raise.Event();51Testing软件测试网7LT4k+az-_d@
//断言事件被触发
,n1Z };AA7SMg*Oy0Assert.IsTrue(eventWasRaised);

TAG:

 

评分:0

我来说两句

Open Toolbar