mock.patch修饰符:缺少1个必需的位置参数:“self”

4
我正在尝试在测试方法运行期间,修补settings模块中的一个变量:
from unittest import mock

class Test(...):

    @mock.patch('settings.TARGET_SCORES_PER_SECTION', True)
    def test_register_user(self):

我遇到了这个错误:
ERROR: tests.test_user.transplant_class.<locals>.C (test_register_user)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.6.5_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py", line 1179, in patched
    return func(*args, **keywargs)
TypeError: test_register_user() missing 1 required positional argument: 'self'

我尝试了不同的方法,但没有找到解决方案。

我做错了什么?

这个版本运行得很好:

    def test_register_user(self):
        with mock.patch('settings.TARGET_SCORES_PER_SECTION', True):
            self._test_register_user()

    def _test_register_user(self):

我希望使用修饰器的方式能够达到同样的效果。


不要忘记导入 settings 并将修补后的模拟对象添加到测试函数参数中,就像这样 def test_register_user(self, mock_target_scores): - progmatico
根据我的测试,你们两个的陈述都是错误的。 - warvariuc
错误有点简略。你测试了哪些代码?先检查我的答案,希望能帮到你。 - progmatico
事实上,这是错误的,因为您需要修补整个设置,而不是属性。如果您不想模拟整个设置模块,而只想模拟属性(使用PropertyMock),请参见此处获取替代方案。 - progmatico
虽然我的答案在这两种形式中都可以工作,但是我无法通过错误地更改补丁和 def 参数来重现您的确切错误消息。您有可能只是忘记了将 self 放入类方法中吗? - progmatico
显示剩余2条评论
1个回答

0

如果我理解你想做什么,这将为你提供如何实现它的思路。

我已经让settings.py只保存对TARGET_SCORES_PER_SECTION赋值为False。在补丁中需要提到 __main__ 只是一个小麻烦,它不能接受非限定名称。

import unittest
from unittest import TestCase
from unittest.mock import patch
import settings


def register_user():
    return settings.TARGET_SCORES_PER_SECTION


class Test(TestCase):

    @patch('__main__.settings')
    def test_register_user(self, mock_settings):
        # setup your mock
        mock_settings.TARGET_SCORES_PER_SECTION = True
        # test your function
        self.assertTrue(register_user())

if __name__ == '__main__':
    unittest.main()

编辑:

我认为为了更好地理解OP的问题,如果您愿意,上面的代码也可以更改为以下内容:

import unittest
from unittest import TestCase
from unittest.mock import patch
import settings


def register_user():
    return settings.TARGET_SCORES_PER_SECTION


class Test(TestCase):

    @patch('settings.TARGET_SCORES_PER_SECTION', True)
    def test_register_user(self):
        self.assertTrue(register_user())

    if __name__ == '__main__':
        unittest.main()

Truenew 参数。该对象用于修补您在修补字符串中提到的位置。这可能更加优雅,但需要您手头有一个配置好的模拟对象。


2
你的解决方案可行,但只是一个变通方法。我的问题是关于使用mock.patch作为装饰器的,根据文档传递new参数。将模拟对象作为函数参数传递并在函数中设置它看起来不太好。请查看我更新后的问题。 - warvariuc
@warvariuc,但这不是我做的(不是我的选择)。它是一个装饰器功能。装饰器更改您的测试函数签名以添加模拟参数。甚至可以堆叠装饰器,它们将添加所有模拟到签名中。即使您没有在内部使用它们,也必须在def中编写它们。这就是为什么您会收到关于“self”的错误的原因。 - progmatico
你也可以装饰一个类。这将改变所有测试方法的签名。 - progmatico
实际上这是一个选择,抱歉。从文档中可以看到,“如果将patch()用作装饰器并省略new参数,则创建的mock对象会作为额外的参数传递给被装饰的函数。如果将patch()用作上下文管理器,则由上下文管理器返回创建的mock对象。” - progmatico
2
我找到了问题所在。它是 nose2 测试运行程序导致的。当我想测试一个单独的 TestCase 方法时 (./manage.sh runtests -r -v tests.test_user.Test.test_register_user),它会传递一个未绑定到 self 的函数。我还没搞清楚为什么会这样以及如何修复,但这已经是另一个问题了。mock.patch 正常工作。感谢您的时间。 - warvariuc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接