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 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.
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 toNone
. - 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.
- unsafe: By default if any attribute starts with assert or assret will raise an AttributeError. Passing
unsafe=True
will allow access to these attributes.
New in version 3.5. - 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.