python模拟框架

pyforge的Python项目详细描述


…图片::https://secure.travis ci.org/vmalloc/pyforge.png


==


forge是python的模拟库。它的灵感大部分来自mox(http://code.google.com/p/pymox)。它的目标是简单,但功能仍然丰富,并为使用模拟方法的单元测试提供最大的灵活性。


运行forge的验收套件
====forge的所有验收测试都在根目录下的tests/目录中。它们需要unittest2或内置的unittest模块(2.7/3.2及更高版本)。

与您已经知道的大多数软件包几乎相同:python setup.py install


usage
=



basics
----
forge主要创建模拟对象和函数存根,但风格多种多样。使用forge总是从创建一个"模拟管理器"开始,其中的*forge*类:

>>;>;来自forge import forge
>;>;forge_manager=forge()


它通常用于创建mock::

>;>;类someClass(对象):
…定义F(自,A,B,C):
…pass
>;>;mock=锻造经理。创建mock(someClass)
>;>;mock
<;mock of"someClass">;

您可以记录您希望mock执行的操作,然后重新播放,而forge则跟踪发生的操作并确保其正确无误:forge-manager.is-u recording()

>;
>;mock.f(1、2、3)doctest:+省略号
<;…>;
>;<;..>;
>;>forge_manager.replay()
>;>forge_manager.is_recording()
false
>;>forge_manager.is_replaying()
true
>;>mock.f(1,2,3)
>;>;>forge_manager.verify()#这验证了不再需要调用

从头开始工作,您可以始终执行::

>;forge-manager.reset()

定义一些函数(a,b,c):
…pass
>;>stub=forge_manager.create_function_stub(some_func)

重放时,调用必须与原始调用匹配,因此您不必太担心与函数签名有关的意外。

<品牌/型号>…mock.f(1,2,3)doctest:+省略号
…mock.f(3,4,5)doctest:+省略号
<;..>;
>;
>;,带有forge_manager.verified_replay_context():
…mock.f(1,2,3)doctest:+省略号
…mock.f(3,4,5)doctest:+省略号

failures and unexpected events
(当意外事件发生时,会引发异常,解释发生的情况):

>
>;stub=forge\u manager.create_function_stub(some_func)
>;>;>;存根(1,2,3)doctest:+省略号
<;…>;
>;>;forge_manager.replay()
>;>;存根(1,2,4)doctest:+忽略异常详细信息
回溯(最近一次调用最后一次):

未预料的调用:调用了意外的函数!(期望值:+,得到:-)
-一些函数(a=1,b=2,c=4)
?^
+一些函数(a=1,b=2,c=3)
?^
>;>;forge_manager.reset()

在某些情况下,这已经足够了,但是如果您需要更多关于调用记录和重播位置的信息,则可以打开调试信息:

>;>;forge_manager.debug.enable()
>;stub=forge_manager.create_函数存根(某些函数)
>;>stub(1,2,3)<;..>;
>;>forge_manager.replay()
>;>stub(1,2,4)回溯(最近的最后一次调用):

未预料的调用:意外的函数调用预计起飞时间!(应为:+,得到:-)
记录自…
回放自…
-某些函数(a=1,b=2,c=4)
?^
+一些函数(a=1,b=2,c=3)
?^
>;>;forge_manager.reset()
>;>;forge_manager.debug.disable()

由于有时这是一种非常常见的模式,您还可以通过在运行测试时将forge_debug环境变量设置为任意值来通过环境变量启用调试。

需要属性设置
----------
仅在记录模式下才允许设置模拟对象的属性。默认情况下,在重播期间设置的属性将触发异常。

但是,在某些情况下,您希望*在重播的某个点设置属性。由于forge setattr/getattr机制的黑客特性,实现这一点的方法是通过一个专用的api通过uuu forge_uu handle:

>;>;mock=forge_u manager.create_u mock(someclass)
>;>;mock.uu forge_u.expect_u setattr("length",20)doctest:+省略号
<;..>;
>;>;forge_manager.replay()
>;>;mock.length=20
>;>;forge_manager.verify()
>;>;forge_manager.reset()

不建议这样做,但有时可能会很有用:

>;>;模拟。>;>;forge\u manager.replay()
>;>;模拟。a=2工作!
>>>forge_manager.reset()

如果要模拟一个*模拟结构*,即一个具有其他对象属性的对象,则可以使用*create_mock_with_attrs*API。如果您为它创建快捷方式,这一点尤其简洁:

>;>;A类(对象):通过
>;>;B类(对象):通过
>;>;C类(对象):通过
>;>;mock=forge\u manager。使用属性
>;>;result=mock(a,b=mock(b,c=mock(c))创建模拟
>;>;result.b.c doctest:+省略号
<;模仿"c">;


操作
----
在存根上等待调用时,可以控制调用发生*时发生的情况。支持的情况有:

-控制返回值::


-调用另一个函数(无参数)::


-调用具有特定参数/关键字参数的另一个函数::

my_stub(1,2,3)。和_call(callback,args=(100,200),kwargs={some_arg':20})


-调用另一个函数(带有调用的参数)::


my_stub(1,2,3)。和_call_with_args(callback)

-引发异常(在所有回调被触发后发生)::


my_stuB(1,2,3).和_raise(myexception())


comparators
----
如果您不知道函数的参数将获得的确切值,则有时必须使用谓词来帮助您区分有效大小写和无效大小写。首先,我们将提到模拟对象只会将"true"与自身进行比较,因此就模拟比较而言,您不必担心任何时髦的业务。

你在录音,你可以用比较器。例如,只要参数是一个字符串,下面的语句就不关心传递给"name"的是哪个参数::



*``是(x)``:仅当参数是*x*
*``isa(type)`:仅当参数是*x*
*时比较true参数的类型是*type*
*`` regexp matches(regexp,[标志])``:仅当参数是字符串时比较true,并且匹配*regexp*
*`` func(f)``:仅当*f*为参数返回true时比较true与*value*相同,按*将*位*数字放在浮点后
*``contains(element)`:仅当参数
*``strcontains(substring)``中存在*element*时比较true:仅当参数中存在*substring*时比较true,并且参数是字符串
*``haskeyvalue(key,value)``:仅当参数将*key*作为键时比较true,其值为*value*
*`` hasAttributeValue(attr,value)``:与hasKeyValue相同,但对于属性
*`` anything()``:始终比较true
*``和(…)、or(…)、not(c)`:和、或和其他比较器的取反符

将方法和函数替换为stub
----------
forge包括一个用于安装(和稍后删除)stub的机制,而不是普通的方法和函数:

>;>;导入时间
>;>;forge-manager.replace(time,"time").#doctest:+ellipsis
<;..>;
>;>time.time()。和_return(2)
2
>;>forge_manager.replay()
>;2
>;>forge_manager.verify()
>;>forge_manager.restore_all_replacements()
>;>forge_manager.reset()

这当然也适用于方法:

>;>;类MyClass(对象):
…定义f(自身):
…self.g()
…定义G(自身):
…提升notimplementederror()
>;>;instance=myclass()
>;>;forge-manager.replace(instance,"g")\doctest:+省略号
<;..>;
>;>;instance.g();doctest:+省略号
<;..>;
>;>;forge-manager.replay()
>;>;instance.f()
>;>;forge_manager.verify()
>;>;forge_manager.restore_all_replacements()
>;>;forge_manager.reset()

(对象):
…x=2
>;>forge_manager.replace_为(someClass,"x",3)
3
>;>someClass.x
3
>;>forge_manager.restore_all_replacements()
>;>someClass.x
2

退出上下文::

>pass

ordering
--
默认情况下,forge会验证实际调用的顺序是否与记录流相同。
但是,您可以控制它并创建顺序无关紧要的组::


>;>;类someClass(object):
…定义函数(self,arg):
…pass
>;>;mock=锻造经理。创建mock(someClass)
>;>;mock.func(1)doctest:+省略号
<;…>;
>;>;mock.func(2)doctest:+省略号
<;…>;
>;>;mock.func(3)doctest:+省略号
…#到目前为止,订单必须保持
<;..>;
>;>;>;>;与Forge嫒u Manager一起。任何嫒order():嫒doctest:+省略号
…模拟函数(4)
…mock.func(5)
<;…>;
<;…>;
>;>mock.func(6)doctest:+省略号
<;…>;
>;>;mock.func(1)
>;>mock.func(2)
>;>mock.func(3)
>;>mock.func(5)>;>>模拟功能(4)也可以!
>;>;mock.func(6)
>;>;forge-manager.verify()
>;>;forge-manager.reset()



:+省略号
…模拟函数(4)
…使用forge-manager.ordered():
…模拟函数(5)
…模拟函数(6)
…mock.func(7)
<;..>;
<;..>;
<;..>;
<;..>;

>;gt;对于i in(5、6、7、4):
…_=mock.func(i)
>;>forge_manager.verify()
>;>forge_manager.reset()


def foo(self,arg):
…通过
…定义栏(self,arg):
…pass
>;>;mock=forge廑manager.create廑mock(someClass)
>;>;带forge廑manager.interleaved廑order():廑doctest:+省略号
…使用forge-manager.ordered():
…mock.foo(1)
…模拟.foo(2)
…使用forge-manager.ordered():
…模拟条(1)
…模拟。酒吧(2)
><;..>;
<;..>;
>;
<;..>;
>;
>;
>;
>;
>;>;Forge-u-Manager.replalay;
>;>>;模拟。foo(1)
>;模拟。酒吧(1)
>>;模拟。foo(2)
>>;模拟。酒吧(2)
>>;Forge.forge.forge>>;
>;
>;
>;>;
>;
>;>;>;>;forge_manager.reset()


上述期望也将按以下顺序工作:

>;
forge_manager.interleaved_order():\doctest:+省略号
…使用forge-manager.ordered():
…mock.foo(1)
…模拟.foo(2)
…使用forge-manager.ordered():
…模拟条(1)
…模拟酒吧(2)
><;..>;
<;..>;
>;
<;..>;
>;
>;
>;
>;
>;>;forge-u-manager.replay;
>;>>;模拟酒吧(1)
>>>>;模拟酒吧(2)
>>>>;>;mock.foo(1)
>>>>>>;>>>>;mock.foo(2)
>>>;>>>;>>;
>;>;
>;
>_manager.verify()
>;>;forge_manager.reset()



有一些丑陋的方法可以做到这一点::

>>>类myobj(对象):
…定义f(自身):
…通过
>;>m=forge_manager.create_mock(myobj)
>;>m.f=lambda:2讨厌!

当然缺点是:

*F存在的事实没有得到验证。它的签名也不能用这个方法验证。
*lambda的很难看,当你想使用异常时它会变得更难看。

*everywhere()*到rescue-它是一个可以在预期的方法上调用的方法,导致调用被接受,签名被检查并执行。但是,与常规录制不同,它希望在任何时候调用0次或更多次-因此它会达到相同的效果:

>;>m=forge-manager.create-mock(myobj)
>;>m.f().everywhen()。和返回(2)
2
>;>forge-manager.replay()
>;>m.f(
2
>;>;m.f()
2
>;>;forge-manager.verify()
>;>;forge-manager.reset()


multiple*while()*可以使用不同参数指定录制,这将导致"pa"的形式ttren matching"用于请求的调用(每个调用签名将导致不同的返回值)。


>;>;类obj(object):
…def f(自,值):
…通过
>;>m=锻造经理。创建模拟(obj)
>;>m.f.当(2)时。然后返回(3)
3
>;>forge_manager.replay()
>;>m.f(2)
3
>;>;
注意::everywhere()调用始终应用于记录它们的顺序组。这意味着,一旦订单组被清除,记录在其中的所有*when*将自动"忘记",并且在重播时将不再被接受。

只是验证你在重播时是否坚持。这对于构建一个尚不存在的接口的原型非常有用。这是在Forge中通过使用*通配符模拟*完成的:

>;>mock=forge-mu manager.创建"通配符模拟"
>;>mock
<;>;>stub=forge-mu manager.创建"通配符函数"stub()
>;<;stub for'<;<;通配符>;>;>;
>>>gt;>;>;mock.f();doctest;>;
>;
>>;>;>;mock.g(1,2,3,d=4)mock.g(1,2,3,d=4)doctest;>;>;
>>;>;>;>;>;>;>;>;>;>;>;>;>;stub;>;>;stub;>;>;>;>;doctest;>;>;>;>;>;>;>;>;>;>;>;35;doctest:+省略号
<;..>;
>;>>forge_manager.replay()
>;>>mock.f()
>;>>mock.g(1、2、3、d=4)
>;>stub()
>;>stub(1、2、3、d=4)
>;>forge_manager.reset()

类,而不是对象。forge允许使用*create_class_mock*api::

>;>类myclass(对象):
…定义初始化(self,a,b,c):
…通过
…def正则方法(self):
…通过
…@类方法
…定义一些类方法(cls):
…通过
…@静态法
…定义一些静态方法:
…pass
>;>class_mock=forge_manager.create_class_mock(myclass)
>;
>;class_mock
<;class mock of‘myclass’>;
>;>class_mock.regular_method();doctest:+ignore_exception_detail
回溯(最近一次调用):签名异常:…
>>gt;类模拟。某些类模拟:<;..>;
>;>class模拟;某些静态模拟:<;..>;
>;
>;>fake模拟实例=forge模拟管理器。创建模拟(myclass)
>;>class模拟(1、2、3)。返回(fake模拟w_u实例)doctest:+省略号
<;..>;
>;>forge_manager.replay()
>;>class_mock.some_class_method()
>;>class_mock.some_static_method()
>;>assert class_mock(1,2,3)是假的新实例
>;>>>>forge_manager.reset()

定义初始化(自我,文件名):
…self.f=打开(文件名,"rb")
…def read(self,size):
…引发notimplementederror()
…def日志(self,buffer):
…引发notimplementederror()
…def read_和_log(self,size):
…数据=自读(大小)
…自我日志(数据)
…返回数据

现在,假设您要为read_和_log编写一个测试,同时模拟read()和log()的行为。这很常见,因为有时类中的方法有很多副作用,这些副作用在编写测试时很难查明。一个简单的方法是创建一个文件对象,并用存根替换read()和log()(见上文)。这很好,但问题在于类构造,它打开一个文件进行读取。

陆上通信线。对于这种情况,forge有混合模拟::

>;>mock=forge_manager.create_hybrid_mock(file)
>;>mock.read(20)。and_return("data")doctest:+ellipsis
'…'
>;>mock.log("data")doctest:+ellipsis
<;..>;
>;>forge_manager.replay()
>;>assert mock.read庘u and庘u log(20)="data"
>;>forge庘u manager.verify()
>;
它们在录制过程中表现为常规模拟,但在重播过程中调用任何尚未录制的方法都将调用模拟上的原始方法,从而在隔离环境中对其进行测试。

定义初始化(自,参数):
…引发notimplementederror()
…@类方法
…def构造函数(cls):
…return cls(1)

>;>;mock=锻造经理。创建混合锻造经理。创建混合锻造经理(someclass)
>;>;预期锻造经理。创建哨兵(sentinel)
>;>;mock(1)。和返回(预期锻造经理)。doctest:+省略号
<;..>;
>;>;>eplay()
>;>got_return_value=mock.constructor()
>;>got_return_value是预期的_return_value
true

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java如何将jasper集成到jhipster项目中   java无法忽略lombok注释   关于tomcat日志的java问题   java@Autowired未设置字段>NullPointerException   GUI提交按钮不工作   java气泡和选择排序   java如何编写规则来匹配两个数组?   java如何找出某个字符在字符串中的第一次、第二次或第三次出现?   java通过字符串引用id   javascript在网络视图中加载在线图表   java保留web应用程序中用户更改的日志   在安卓中尝试使用Mandrill SMTP发送电子邮件时出现java错误   用java语言将a2b4c5等字符串转换为AABBCCCCC的程序是什么?   java无需TODO即可删除所有注释   java JMX MBean在应用程序部署时自动注册   java如何使用JSON解析从任何url解析数据   java@transactional注释类使用代理包装,但未创建事务   JavaFx转换和打印