在Python中使用mock模块进行单元测试

为何须要Mock

假设如今系统有两个模型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

参考资料

官方文档 https://docs.python.org/3/library/unittest.mock.html

相关文章
相关标签/搜索