Python:单元测试中的模拟

标签 python unit-testing mocking

我有类似的情况:

class BaseClient(object):
    def __init__(self, api_key):
        self.api_key = api_key
        # Doing some staff.

class ConcreteClient(BaseClient):
    def get_some_basic_data(self):
        # Doing something.

    def calculate(self):
        # some staff here
        self.get_some_basic_data(param)
        # some calculations

然后我想使用 get_some_basic_data 函数的模拟来测试 calculate 函数。

我正在做这样的事情:

import unittest
from my_module import ConcreteClient

def my_fake_data(param):
    return [{"key1": "val1"}, {"key2": "val2"}]

class ConcreteClientTest(unittest.TestCase):
    def setUp(self):
        self.client = Mock(ConcreteClient)

    def test_calculate(self):

        patch.object(ConcreteClient, 'get_some_basic_data',
                     return_value=my_fake_data).start()
        result = self.client.calculate(42)

但它没有按我的预期工作。正如我所想,self.get_some_basic_data(param)my_fake_data 函数返回我的列表,但看起来它是仍然是一个 Mock 对象,这对我来说是不期望的。

这里出了什么问题?

最佳答案

您在这里面临两个主要问题。引发您当前遇到的问题的主要问题是您实际上是如何进行模拟的。现在,由于您实际上是在为 ConcreteClient 修补对象,因此您需要确保您仍在使用真实 ConcreteClient,但是模拟测试时要模拟的实例的属性。您实际上可以在文档中看到这个插图。不幸的是,没有确切的行的明确 anchor ,但如果您点击此链接:

https://docs.python.org/3/library/unittest.mock-examples.html

该部分指出:

Where you use patch() to create a mock for you, you can get a reference to the mock using the “as” form of the with statement:

引用代码为:

class ProductionClass:
    def method(self):
        pass

with patch.object(ProductionClass, 'method') as mock_method:
    mock_method.return_value = None
    real = ProductionClass()
    real.method(1, 2, 3)

mock_method.assert_called_with(1, 2, 3)

这里要注意的关键事项是如何调用所有内容。请注意,该类的真实实例已创建。在您的示例中,当您执行此操作时:

self.client = Mock(ConcreteClient)

您正在创建一个 ConcreteClient 上指定的 Mock 对象。因此,最终这只是一个 Mock 对象,它保存 ConcreteClient 的属性。您实际上不会持有 ConcreteClient真实实例。

解决这个问题。只需在修补对象后创建一个真实实例即可。另外,为了让你的生活更轻松,这样你就不必手动启动/停止你的 patch.object,使用上下文管理器,它会为你省去很多麻烦。

最后,你的第二个问题是你的return_value。您的 return_value 实际上返回了 unused my_fake_data 函数。您实际上需要数据本身,因此它需要是该函数的返回。您可以将数据本身作为您的return_value

考虑到这两个更正,您的测试现在应该如下所示:

class ConcreteClientTest(unittest.TestCase):

    def test_calculate(self):

        with patch.object(ConcreteClient, 'get_some_basic_data',
                     return_value=[{"key1": "val1"}, {"key2": "val2"}]):

            concrete_client = ConcreteClient(Mock())
            result = concrete_client.calculate()

        self.assertEqual(
            result,
            [{"key1": "val1"}, {"key2": "val2"}]
        )

我冒昧地在 calculate 中实际返回了 get_some_basic_data 的结果,只是为了进行比较。我不确定您的真实代码是什么样的。但最终,上面说明了您应该如何执行此操作的测试结构。

关于Python:单元测试中的模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44556468/

相关文章:

c# - 代码生成工具,为单元测试创​​建 C# 适配器类?

c# - 无法在单元测试中将 void 分配给隐式类型变量

python:两个配对列表的索引/分页

python - 如何限制对未定义的类/实例变量的访问?

ios - 单元测试 iOS 中的子模块静态函数? ( swift 4 - Xcode)

java - 是否可以在进程中启动 Zookeeper 服务器实例,例如用于单元测试?

python - 使用 Python Mock 库监视内部方法调用

python - 在 python 中使用组合来处理非常大的序列

python - 删除特定的键但不删除值(列表),因此它成为上键的元素

java - 如何模拟参数化构造函数?