python - 如何使用模拟作为函数参数在 Python 中修补常量

标签 python unit-testing mocking constants patch

我正在尝试了解使用 mock.patch 在 Python 中修补常量的不同方法。 我的目标是能够使用我的测试类中定义的变量作为常量的修补值。

我发现这个问题解释了如何修补常量: How to patch a constant in python 这个问题解释了如何在补丁中使用 self : using self in python @patch decorator

但是从第二个链接,我无法使用 testTwo 方法(提供模拟作为函数参数)工作

这是我的简化用例:

mymodule.py

MY_CONSTANT = 5

def get_constant():
    return MY_CONSTANT

test_mymodule.py

import unittest
from unittest.mock import patch

import mymodule

class Test(unittest.TestCase):

    #This works
    @patch("mymodule.MY_CONSTANT", 3)
    def test_get_constant_1(self):
        self.assertEqual(mymodule.get_constant(), 3)

    #This also works
    def test_get_constant_2(self):
        with patch("mymodule.MY_CONSTANT", 3):
            self.assertEqual(mymodule.get_constant(), 3)

    #But this doesn't
    @patch("mymodule.MY_CONSTANT")
    def test_get_constant_3(self, mock_MY_CONSTANT):
        mock_MY_CONSTANT.return_value = 3
        self.assertEqual(mymodule.get_constant(), 3)
        #AssertionError: <MagicMock name='MY_CONSTANT' id='64980808'> != 3

我的猜测是我不应该使用 return_value,因为 mock_MY_CONSTANT 不是一个函数。那么我应该使用什么属性来替换调用常量时返回的值?

最佳答案

我认为您正在尝试了解单元测试、模拟对象以及如何替换被测代码中常量的值。

我将从您关于修补常量的具体问题开始,然后我将描述一种更通用的替换常量值的方法。

您的具体问题是关于 patch("mymodule.MY_CONSTANT", 3)patch("mymodule.MY_CONSTANT") 之间的区别。根据docs ,第二个参数是 new,它包含将被修补的替换值。如果你将它保留为默认值,那么一个 MagicMock 对象将被修补。正如您在问题中指出的那样,MagicMock.return_value 适用于函数,但您没有调用 MY_CONSTANT,因此永远不会使用返回值。

我对这个问题的简短回答是,“不要使用 MagicMock 来替换常量。”如果出于某种原因,您迫切希望这样做,您可以覆盖唯一调用该常量的方法,即它的 __eq__() 方法。 (我想不出这是个好主意的任何场景。)

import unittest
from unittest.mock import patch

import mymodule

class Test(unittest.TestCase):

    #This works
    @patch("mymodule.MY_CONSTANT", 3)
    def test_get_constant_1(self):
        self.assertEqual(mymodule.get_constant(), 3)

    #This also works
    def test_get_constant_2(self):
        with patch("mymodule.MY_CONSTANT", 3):
            self.assertEqual(mymodule.get_constant(), 3)

    #This now "works", but it's a horrible idea!
    @patch("mymodule.MY_CONSTANT")
    def test_get_constant_3(self, mock_MY_CONSTANT):
        mock_MY_CONSTANT.__eq__ = lambda self, other: other == 3
        self.assertEqual(mymodule.get_constant(), 3)

现在是更一般的问题。我认为最简单的做法不是改变常量,而是提供一种覆盖常量的方法。改变常量对我来说感觉不对,因为它被称为常量。 (当然这只是一个约定,因为 Python 不强制执行常量值。)

以下是我将如何处理您正在尝试做的事情。

MY_CONSTANT = 5

def get_constant(override=MY_CONSTANT):
    return override

然后您的常规代码可以调用 get_constant(),您的测试代码可以提供覆盖。

import unittest

import mymodule

class Test(unittest.TestCase):
    def test_get_constant(self):
        self.assertEqual(mymodule.get_constant(override=3), 3)

随着您的代码变得越来越复杂,这会变得更加痛苦。如果您必须通过一堆层来传递覆盖,那么它可能不值得。但是,这可能表明您的设计存在问题,导致代码更难测试。

关于python - 如何使用模拟作为函数参数在 Python 中修补常量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42275139/

相关文章:

unit-testing - 在帮助测试的辅助库中使用控制语句不好吗?

methods - PHPUnit mocks - 调用断言方法

python - 卸载 easy_install、pip、pip3、virtualenv 和 virtualenvwrapper

python - 如何使用 datetime Python 模块计算距当前日期六个月的日期?

java - 即使在排除单元测试之后,单元测试也会在集成测试阶段执行

C# Visual Studio 单元测试,模拟客户端 IP 地址

testing - 来自同一JSON映射的Wiremock返回成功或错误响应

python - 获取具有多个不明确元素的数组的真值以进行蛋白变换

python相当于cmd的 "for %%a in (%*) do"

unit-testing - ant构建脚本的单元测试和代码覆盖率