假设如今系统有两个模型A和B,其中A依赖B(例如A,B都是函数,A函数体内调用了B函数),可是B还没完成,或者根本就不在控制以内;html
这时候又须要对A的功能进行单独测试,就须要使用mock对象,模拟出一个假的fake_B模块,虽然这个fake_B模块是假的,可是咱们能够经过对它的行为进行定制来使他可以看起来“像”B模块的功能,使A依赖fake_B,来对A的功能进行测试。python
同时,因为fake_B是彻底可控的,除了能够定制B的属性,返回值以外,还能够对B模块的使用状况进行测试。ide
而在Python中可使用mock和unittest.mock来产生这样的mock对象。函数
首先须要建立一个mock对象:测试
>>>from mock import MagicMock >>>fake_obj = MagicMock()
而后能够经过return_value设定它的返回值,当你调用这个mock对象时返回,返回值能够是任何类型,变量,函数,类对象均可以。code
>>>fake_obj.return_value = 'This is a mock object' >>>fake_obj() 'This is a mock object'
也能够经过side_effect指定它的反作用,这个反作用就是当你调用这个mock对象是会调用的函数,也能够选择抛出一个异常,来对程序的错误状态进行测试。orm
>>>def b(): ... print 'This is b' ... >>>fake_obj.side_effect = b >>>fake_obj() This is b >>>fake_obj.side_effect = KeyError('This is b') >>>fake_obj() ... KeyError: 'This is b'
同时也能够以一个对象base为基础,拓展mock,使得mock得到base的全部属性和方法(但仅是接口相同,没有实现),当调用不属于base的属性和方法时,会抛出AttributeError。这须要在建立mock对象的时候,在spec参数指定,前面的返回值和反作用也能够在建立时进行指定。htm
>>>class B: ... def __init__(self): ... self.a = 'a' ... >>>b = B() >>>fake_obj = MagicMock(b) <MagicMock spec='B' id='4370614160'> >>>fake_obj.a 'a' >>>fake_obj.b AttributeError: Mock object has no attribute 'b'
还有一点须要强调的就是,若是要模拟一个对象而不是函数,你能够直接在mock对象上添加属性和方法,而且每个添加的属性都是一个mock对象,也就是说能够对这些属性进行配置,而且能够一直递归的定义下去。对象
>>>fake_obj.fake_a.return_value = 'This is fake_obj.fake_a' >>>fake_obj.fake_a() 'This is fake_obj.fake_a'
在对mock对象进行必定的配置以后就是使用mock对象进行测试,这个模块采用的‘action-assertion’的方式进行测试,即先运行,再经过断言进行测试。例如能够测试mock对象是否被调用,调用了几回,如何被调用等等,mock对象自己提供了丰富的断言方法,官方文档中提供了详尽的描述。blog
>>>fake_obj = MagicMock(return_value = 1) >>>fake_obj.assert_called() AssertionError: Expected 'None' to have been called. >>>fake_obj() >>>fake_obj.assert_called() >>> >>>fake_obj(1,2,3,key=1) >>>fake_obj.assert_called_with(1,2,3,key=1) >>>
同时mock库提供了patch函数来简化mock对象对原对象的替换。patch能够做为装饰器或者上下文管理器使用,这意味着在装饰的函数和上下文管理器中,对应的类会被替换为mock对象。
>>>from mock import patch >>>@patch('__main__.SomeClass') ... def function(normal_argument, mock_class): ... print(mock_class is SomeClass) ... >>>function(None) True