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类的解释

doctest python3 模块 python unittest mock_ide

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=Noneside_effect=Nonereturn_value=DEFAULTwraps=Nonename=Nonespec_set=Noneunsafe=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.
    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_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.