繁体   English   中英

使用 Python 模拟来监视对现有 object 的调用

[英]Using Python mock to spy on calls to an existing object

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

# 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()

如果模拟上的所有方法调用都可以在没有样板的情况下自动转发到实时 object,那就太好了。

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

import mock

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

似乎可以正常使用函数,方法和属性,但不适用于类或实例属性。

请参阅文档

您可以使用Python的模拟模块在实例方法上进行Spying时的建议使用patch.object(wraps=obj_instance)

例如:

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)

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

import inspect 

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.active_attr = spy_mock(server.active_attr)

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

请注意,此解决方案符合 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()

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

from unittest import mock, TestCase

import foo_module

class FooTest(TestCase):

    @mock.patch(f'{foo_module.__name__}.datetime', wraps=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并使用date.today以外的许多其他datetime函数。

如果你使用 pytest,pytest-mock package 有一个方便的间谍object。

class Foo(object):
    def bar(self, v):
        return v * 2

def test_spy_method(mocker):
    foo = Foo()
    spy = mocker.spy(foo, 'bar')
    assert foo.bar(21) == 42

    spy.assert_called_once_with(21)
    assert spy.spy_return == 42

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM