【问题标题】:Pytest - mocking class instance passed as an argumentPytest - 作为参数传递的模拟类实例
【发布时间】:2021-09-15 12:02:17
【问题描述】:

假设我有一个简化的课程Object:

class Object:
    def __init__(self, data):
        self.data = data

    def get_data():
        return data.copy()

以及独立功能foo:

def foo(obj: Object):
    copied_data = obj.get_data()
    ...

我想测试foo 并使用带有模拟Object 实例的夹具作为参数传递给foo。我希望模拟对象返回一些预定义的数据,所以我也需要模拟它的方法。

我应该如何使用 pytest 以“正确”的方式执行此操作?我不确定如何组合模拟和固定装置。

【问题讨论】:

  • 没有什么可嘲笑的。 foo 已经获取了您可以提供的任何对象,因此只需创建一个包含测试所需数据的对象,然后在调用时将其传递给 foo。
  • @chepner 我只需要这些数据,但同时我需要传递整个对象。跳过对象的整个创建不是有益的吗?实际上类和构造函数显然比这里更复杂
  • 如果创建Object 的实例比创建模拟更昂贵,那么您可能在Object.__init__ 中做的工作太多。
  • 但也许您只是在谈论将Mock 的实例作为参数传递给foo,而不是Object 的实际实例。那里也没什么特别的。

标签: python python-3.x mocking pytest


【解决方案1】:

使用with statement 和示例patching 文档:

除了装饰器patch() 可以用作with 语句中的上下文管理器:

...

>>> class Class:
...     def method(self):
...         pass
...
>>> with patch('__main__.Class') as MockClass:
...     instance = MockClass.return_value
...     instance.method.return_value = 'foo'
...     assert Class() is instance
...     assert Class().method() == 'foo'
...

我们可以在fixture 中使用patch(),然后在模拟实例中使用yield。

src.py

class Object:
    def __init__(self, data):
        print("Real object initialized")
        self.data = data

    def get_data(self):
        print("Real object get_data")
        return self.data.copy()


def foo(obj: Object):
    print("Object instance:", obj)
    copied_data = obj.get_data()
    return copied_data

test_src.py

from unittest.mock import patch
import pytest

from src import foo, Object


@pytest.fixture
def object_instance():
    with patch('src.Object') as MockClass:
        instance = MockClass.return_value
        instance.get_data.return_value = 'bar'
        yield instance


def test_real_impl():
    object_instance = Object([1, 2])
    assert foo(object_instance) == [1, 2]


def test_mock_impl(object_instance):
    assert foo(object_instance) == 'bar'

输出:

$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
_____________________________________________________________________________________________ test_real_impl ______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Real object initialized
Object instance: <src.Object object at 0x7fb59ef83820>
Real object get_data
_____________________________________________________________________________________________ test_mock_impl ______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Object instance: <MagicMock name='Object()' id='140418032072736'>
2 passed in 0.06s
  • 如您所见,我们能够创建一个模拟的 Object 并定义其方法的返回值。

【讨论】:

  • 谢谢!我尝试了您的解决方案,并在我的代码中发现了一个愚蠢的错误。显然,不必导入单元测试并使用产量。将 mocker 从 pytest 传递到夹具并调用 instance = mocker.patch('__main__.Class') 工作正常
猜你喜欢
  • 2021-03-09
  • 2013-12-17
  • 2013-06-23
  • 1970-01-01
  • 2011-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多