48

我正在使用 Python 模拟模块进行测试。我想用模拟替换活动对象,并自动将对模拟对象的所有调用转发到原始对象。我认为这在标准测试术语中被称为“间谍”。目前我正在做一个测试:

# Insert a mock replacement
orig_active_attr = server.active_attr
server.active_attr = mock.Mock()

# Set up side effects to 'proxy' to the original object
server.active_attr.meth1.side_effect = orig_active_attr.meth1
server.active_attr.meth2.side_effect = orig_active_attr.meth2

# Call the method being tested
server.method_being_tested()

# Assert stuff on the mock.
server.active_attr.meth2.assert_called_once()

如果 mock 上的所有方法调用都可以在没有样板的情况下自动转发到 live 对象,那就太好了。

4

5 回答 5

48

我似乎偶然发现了解决方案:

import mock

class A(object):
    def meth(self, a):
        return a
a = A()
ma = mock.Mock(wraps=a)

似乎适用于函数、方法和属性,但不适用于类或实例属性。

请参阅文档

于 2013-11-21T20:12:30.683 回答
28

您可以patch.object(wraps=obj_instance)按照Spying on instance methods with Python's mock module中的建议使用。

例如:

from mock import patch

class Foo(object):
    def bar(self, x, y):
        return x + y + 1

def test_bar():
    foo = Foo()
    with patch.object(foo, 'bar', wraps=foo.bar) as wrapped_foo:
        foo.bar(1, 2)
        wrapped_foo.assert_called_with(1, 2)
于 2017-03-28T09:13:58.320 回答
4

以下是如何模拟 only datetime.date.today(),将其余datetime调用转发到datetime模块:

from unittest import mock, TestCase

import foo_module

class FooTest(TestCase):
  
    @mock.patch(f'{foo_module.__name__}.datetime', wraps=foo_module.datetime)
    def test_something(self, mock_datetime):
        # mock only datetime.date.today()
        mock_datetime.date.today.return_value = datetime.date(2019, 3, 15)
        # other calls to datetime functions will be forwarded to original datetime

foo_module导入datetime和使用datetimedate.today.

于 2019-03-15T16:27:04.170 回答
2

您可以使用一个简单的函数来遍历所有方法并配置您的模拟

def spy_mock(instance):
    members = inspect.getmembers(instance, inspect.ismethod)
    attrs = {'%s.side_effect' % k:v for k,v in members}
    return mock.Mock(**attrs)

用法是

import inspect
from unittest import mock

class ActiveAttr:

    def meth2(self):
        print("Meth2 called")

class Server:

    def __init__(self):
        self.active_attr = ActiveAttr()

    def method_being_tested(self):
        self.active_attr.meth2()


def spy_mock(instance):
    members = inspect.getmembers(instance, inspect.ismethod)
    attrs = {'%s.side_effect' % k:v for k,v in members}
    return mock.Mock(**attrs)

server = Server()
server.active_attr = spy_mock(server.active_attr)

server.method_being_tested()

server.active_attr.meth2.assert_called_once()
于 2013-09-18T15:04:28.820 回答
1

扩展Wes McKinney 的模式(通过 Wilfred Hughes Answer),这里是如何监视对象的方法/成员,其中对象被导入到被测模块中

请注意,此解决方案与 Python 2.x 兼容!

被测模块

import object

def function_in_module_under_test():
  object.method_from_imported_object()

在 method_from_imported_object 上测试间谍断言

from unittest import mock

import module_under_test    

def test_method(self):
  with mock.patch.object(
    module_under_test.object,
    "method_from_imported_object", 
    module_under_test.object.method_from_imported_object,
  ) as spy_method_from_imported_object:

    # Demonstrate that subsequent spy asserts here, can be trusted
    spy_method_from_imported_object.assert_not_called()
    
    # now Test
    module_under_test.function_in_module_under_test()
    spy_method_from_imported_object.assert_called_once()
于 2021-08-13T01:00:17.510 回答