unittest之mock
1.使用python mock
在python3中,它是标准模块,直接通过from unittest import mock就能使用,在python2.4~2.7中,需要通过安装使用。
mock概念:可以这样理解,现在有两个函数,函数1和函数2,函数1内部调用了函数2,现在对函数1进行单元测试。假设单元测试的结果是正确的,这个时候,
修改函数2,就会间接导致函数1的结果发生变化,从而导致函数1的单元测试结果失败,但是函数1是没有问题的,问题出在函数2,因此函数1的单元测试结果不
应该失败,这个时候就可以用mock来解决这个问题。
解决方案:不直接调用函数2,而是给函数2假定一个符合逻辑的返回值,无论函数2怎么改,都不会影响这个假定的返回值,这样就能确保修改函数2不会导致函
数1的单元测试失败。这就是mock的作用。
2.简单的例子
myclass.py
def add_and_multiply(x, y): addition = x + y # add_and_multiply内部调用了multiply multiple = multiply(x, y) return (addition, multiple) def multiply(x, y): return x * y + 3
mork_file.py
import unittest from unittest import mock, TestCase import myclass from myclass import multiply, add_and_multiply class TestCount(TestCase): @mock.patch('myclass.multiply') def test_add_and_multiply(self, mock_multiply): x = 3 y = 5 mock_multiply.return_value = 15 addition, multiple = add_and_multiply(x, y) mock_multiply.assert_called_once_with(3, 5) self.assertEqual(8, addition) self.assertEqual(15, multiple) if __name__ == '__main__': unittest.main()
使用@mock.patch("myclass.multiply"),参数必须是模块.类名,因为在源码中会对.进行切割。
patch()装饰/上下文管理器可以很容易地模拟类或对象在模块测试。在测试过程中,您指定的对象将被替换为一个模拟(或其他对象),并在测试结束时还原。
这里模拟myclass.py文件中multiply()函数。
在def test_add_and_multiply(self, mock_multiply)里面的形参mock_multiply,指的就是myclass.multiply。
mock_multiply.return_value = 15,给mock_multiply假定一个返回值15,
mock_multiply.assert_called_once_with(3, 5),判断mock_multiply是否使用了参数3和5
3.Mock类的解释

Mock类内部没有代码,它继承了两个类,CallableMixin和NonCallableMock,这两个类继承了base
class Mock(CallableMixin, NonCallableMock): """ Create a new `Mock` object. `Mock` takes several optional arguments that specify the behaviour of the Mock object: * `spec`: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an `AttributeError`. If `spec` is an object (rather than a list of strings) then `mock.__class__` returns the class of the spec object. This allows mocks to pass `isinstance` tests. * `spec_set`: A stricter variant of `spec`. If used, attempting to *set* or get an attribute on the mock that isn't on the object passed as `spec_set` will raise an `AttributeError`. * `side_effect`: A function to be called whenever the Mock is called. See the `side_effect` attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns `DEFAULT`, the return value of this function is used as the return value. If `side_effect` is an iterable then each call to the mock will return the next value from the iterable. If any of the members of the iterable are exceptions they will be raised instead of returned. * `return_value`: The value returned when the mock is called. By default this is a new Mock (created on first access). See the `return_value` attribute. * `wraps`: Item for the mock object to wrap. If `wraps` is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn't exist will raise an `AttributeError`). If the mock has an explicit `return_value` set then calls are not passed to the wrapped object and the `return_value` is returned instead. * `name`: If the mock has a name then it will be used in the repr of the mock. This can be useful for debugging. The name is propagated to child mocks. Mocks can also be called with arbitrary keyword arguments. These will be used to set attributes on the mock after it is created. """
参数解析:
name:Mock类的名字,可以通过reper查看;
spec:给Mock对象添加属性和方法,可以是字符串列表或者是类,可以通过dir方法查看属性,访问dir列表中的任何属性都不会报错;
return_value:给Mock对象一个假定的返回值;
side_effect:如果设置了side_effect会覆盖return_value,导致假定的返回值无效;
CallableMixin内部有4个方法,分别是init,_mock_check_sig,__call__,_mock_call
class CallableMixin(Base): def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, parent=None, _spec_state=None, _new_name='', _new_parent=None, **kwargs): self.__dict__['_mock_return_value'] = return_value _safe_super(CallableMixin, self).__init__( spec, wraps, name, spec_set, parent, _spec_state, _new_name, _new_parent, **kwargs ) self.side_effect = side_effect def _mock_check_sig(self, *args, **kwargs): # stub method that can be replaced with one with a specific signature pass def __call__(_mock_self, *args, **kwargs): # can't use self in-case a function / method we are mocking uses self # in the signature _mock_self._mock_check_sig(*args, **kwargs) return _mock_self._mock_call(*args, **kwargs) def _mock_call(_mock_self, *args, **kwargs): self = _mock_self self.called = True self.call_count += 1 _new_name = self._mock_new_name _new_parent = self._mock_new_parent _call = _Call((args, kwargs), two=True) self.call_args = _call self.call_args_list.append(_call) self.mock_calls.append(_Call(('', args, kwargs))) seen = set() skip_next_dot = _new_name == '()' do_method_calls = self._mock_parent is not None name = self._mock_name while _new_parent is not None: this_mock_call = _Call((_new_name, args, kwargs)) if _new_parent._mock_new_name: dot = '.' if skip_next_dot: dot = '' skip_next_dot = False if _new_parent._mock_new_name == '()': skip_next_dot = True _new_name = _new_parent._mock_new_name + dot + _new_name if do_method_calls: if _new_name == name: this_method_call = this_mock_call else: this_method_call = _Call((name, args, kwargs)) _new_parent.method_calls.append(this_method_call) do_method_calls = _new_parent._mock_parent is not None if do_method_calls: name = _new_parent._mock_name + '.' + name _new_parent.mock_calls.append(this_mock_call) _new_parent = _new_parent._mock_new_parent # use ids here so as not to call __hash__ on the mocks _new_parent_id = id(_new_parent) if _new_parent_id in seen: break seen.add(_new_parent_id) ret_val = DEFAULT effect = self.side_effect if effect is not None: if _is_exception(effect): raise effect if not _callable(effect): result = next(effect) if _is_exception(result): raise result if result is DEFAULT: result = self.return_value return result ret_val = effect(*args, **kwargs) if (self._mock_wraps is not None and self._mock_return_value is DEFAULT): return self._mock_wraps(*args, **kwargs) if ret_val is DEFAULT: ret_val = self.return_value return ret_val
NonCallableMock类实现了很多断言方法,它将帮助跟踪测试对象对mock方法的调用。他们能和unittest模块的断言一起工作。能连接到mock或者其方法属性之一。
断言方法
assert_called:检查mock是否被调用;
assert_called_once:检查mock是否只被调用一次;
assert_called_with():检查mock方法是否获得了正确的参数;
assert_called_once_with():检查测试对象是否正确的调用了mock方法,如果调用次数超过1,会引发错误;
assert_any_call():检查测试对象在测试例程中是否调用了测试方法;
assert_has_calls():它查看方法调用的顺序,检查他们是否按正确的次序调用并带有正确的参数。有两个参数,期望调用方法的列表和any_order;
管理Mock
attach_mock():在mock中添加第二个mock对象。这个方法带有两个参数:第二个mock对象和一个属性名称(Name)。
configure_mock():批量的更改mock对象。参数是一个字典,每个键就是要修改的属性。如果对象没有指定的属性,configure_mock()将在mock中添加属性。
mock_add_spec():对mock对象添加新的属性,有两个参数:spec属性和spc_set标志。spce可以是字符串列表或者是类。已添加的属性缺省状态是只读的,
但是通过设置spec_set标志为True,可以让属性可写。
resetMock():恢复mock对象到测试前的状态。它清除了mock对象的调用统计和断言。它不会清除mock对象的return_value和side_effect属性和它的方法属性。
这样做是为了重新使用mock对象避免重新创建mock的开销。
Mock统计
called:当mock对象获得工厂调用时,访问器called返回True,否则返回False;
call_count:返回mock对象被工厂调用的次数;
call_args:返回工厂调用最近使用的参数;
call_args_list:返回一个列表,包含所有使用的参数,第一项为最早的参数;
mothod_calls:报告了测试对象所做的mock方法的调用。它的结果是一个列表,只显示方法调用,不显示工厂调用;
mock_calls:报告了测试对象对mock对象所有的调用。结果是一个列表,工厂调用和方法调用都显示了。
在测试中使用MOCK
MOCK使用方法详解
官方文档:https://docs.python.org/3.6/library/unittest.mock.html
1.Mock和MagicMock的区别
MagicMock继承了Mock的所有方法,并且加入了一些魔法方法,也就是说Mock没有魔法方法,MagicMock除了有Mock的所有方法外,还有一些魔法方法。
2.上下文管理器with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method: ... thing = ProductionClass() ... thing.method(1, 2, 3) ... >>> mock_method.assert_called_once_with(1, 2, 3)
3.使用with patch.dict可以对字典进行重新赋值使用并且可以还原,如果clear设置成False,则字典不会还原
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
4.使用mock的create_autospec可以保证函数的传参和实际的函数是一致的,如果不一致,会报错
>>> from unittest.mock import create_autospec >>> def function(a, b, c): ... pass ... >>> mock_function = create_autospec(function, return_value='fishy') >>> mock_function(1, 2, 3) 'fishy' >>> mock_function.assert_called_once_with(1, 2, 3) >>> mock_function('wrong arguments') Traceback (most recent call last): ... TypeError: <lambda>() takes exactly 3 arguments (1 given)
5.Mock参数详解
class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)
Create a new Mock object. Mock takes several optional arguments that specify the behaviour of the Mock object:
-
spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an
AttributeError.If spec is an object (rather than a list of strings) then
__class__returns the class of the spec object. This allows mocks to passisinstance()tests. -
spec_set: A stricter variant of spec. If used, attempting to set or get an attribute on the mock that isn’t on the object passed as spec_set will raise an
AttributeError. -
side_effect: A function to be called whenever the Mock is called. See the
side_effectattribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returnsDEFAULT, the return value of this function is used as the return value.Alternatively side_effect can be an exception class or instance. In this case the exception will be raised when the mock is called.
If side_effect is an iterable then each call to the mock will return the next value from the iterable.
A side_effect can be cleared by setting it to
None. -
return_value: The value returned when the mock is called. By default this is a new Mock (created on first access). See the
return_valueattribute. -
unsafe: By default if any attribute starts with assert or assret will raise an
AttributeError. Passingunsafe=Truewill allow access to these attributes.New in version 3.5.
-
wraps: Item for the mock object to wrap. If wraps is not
Nonethen calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise anAttributeError).If the mock has an explicit return_value set then calls are not passed to the wrapped object and the return_value is returned instead.
-
name: If the mock has a name then it will be used in the repr of the mock. This can be useful for debugging. The name is propagated to child mocks.

浙公网安备 33010602011771号