2012-09-07 60 views
3

我不明白如何模拟修补程序的作品,它是否能够解决我的问题。蟒蛇模拟库 - 修补类,而单元测试

我有3个文件:与外部接口(a.py),业务逻辑(b.py)和测试(test.py)的通信。我想在运行测试时修补业务逻辑使用的外部接口。

a.py:

class SomeProductionClassINeedPatch(object): 
    name = 'Production Class (communication with some external service)' 
    def do_something(self): 
     print '<some feature with external service>' 

b.py:

import mock 
from src.tmp.mocks.a import SomeProductionClassINeedPatch 

class WorkingClass(object): 
    def some_method_that_uses_external_class(self, *args): 
     external = self._external 
     external.do_something() 

    @property 
    def _external(self): 
     if not hasattr(self, '_ext_obj' or not self._ext_obj): 
      self._ext_obj = SomeProductionClassINeedPatch() 
      print isinstance(self._ext_obj, mock.MagicMock) # False 
     return self._ext_obj 

b = WorkingClass() 
b.some_method_that_uses_external_class() 

test.py:

import mock 
from src.tmp.mocks.b import WorkingClass # class I want to test 

@mock.patch('src.tmp.mocks.a.SomeProductionClassINeedPatch') 
def test_some_method_of_working_class(external_mock=None, *args): 
    o = WorkingClass() 
    o.some_method_that_uses_external_class()  # external interface wasn't patched: <some feature with external service> - but I need mock here! 
    print '<test> - '+str(isinstance(o._external, mock.MagicMock)) # False 

test_some_method_of_working_class() 

我想到的是打电话o.some_method_that_uses_external_class()在测试。 py实际上不会使用外部接口,而是模拟对象。但似乎仍然使用实际的对象。

另外,当我检查外部接口对象的实例无论是在test.py或b.py - 我不能让他们通过isinstance(object,MagicMock)检查,它总是返回false。即使我尝试在b.py(作为类装饰器)中应用相同的修补程序。我究竟做错了什么?

如果有问题,我使用Michael Foord的python 2.7和mock library 1.0。

+1

我不会回答你的问题,但我最近发现自己处于类似的情况,使用各种黑客进行单元测试工作,然后偶然发现了Clean Code Talks视频: http://www.youtube.com/watch? v = -FRm3VPhseI 你可以在哪里找到一些优秀的设计模式,你每个人如何编写易于测试的代码,而且你不需要黑客就可以测试你的代码。我强烈推荐给大家。 – andrean

+0

感谢视频 - 显示非常有帮助。知道很重要的东西 – Serge

+0

别提了,我觉得在我看到它后我达到了启发,并且有一种感觉,omg我必须重写所有我已经完成的东西:) – andrean

回答

0

Where to patch说:

补丁作品(暂时)改变对象的名称指向与另一之一。可以有许多名称指向任何单个对象,因此为了修补工作,您必须确保您修补被测系统使用的名称。

在您的示例中,使用要打补丁的对象的代码位于模块b中。当你给补丁打电话时,这个班级已经被导入到模块b中,所以补丁ab没有影响。你需要,而不是到路上的物体​​在b

@mock.patch('src.tmp.mocks.b.SomeProductionClassINeedPatch') 

这会给你预期的结果,从b第一个电话是打补丁,而从test第二个电话中使用的模拟对象:

# python test.py 
False 
<some feature with external service> 
True 
<test> - True