Декоратор mock.patch: отсутствует 1 обязательный позиционный аргумент: «я»

Я пытаюсь исправить переменную в модуле 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):

И я надеялся использовать его в качестве декоратора будет работать так же.


person warvariuc    schedule 17.04.2019    source источник
comment
Не забудьте импортировать settings и добавить исправленный макет в параметры тестовой функции, как в def test_register_user(self, mock_target_scores):   -  person progmatico    schedule 19.04.2019
comment
Для того, что я проверил, оба ваших утверждения неверны.   -  person warvariuc    schedule 19.04.2019
comment
Неправильно немного минимальное описание. Какой код вы тестировали? Сначала проверьте мой ответ, надеюсь, что это поможет.   -  person progmatico    schedule 19.04.2019
comment
На самом деле это неправильно только потому, что вам нужно пропатчить все настройки, а не атрибут. Или см. здесь для альтернатива, если вы не хотите издеваться над всем модулем настроек, а только над атрибутом (использует PropertyMock)   -  person progmatico    schedule 19.04.2019
comment
Я заметил, что у вас нет части as в операторе with, что означает, что вы не хотите получать доступ к макету, кроме как сделать с ним патч. Что также хорошо для простых фиктивных случаев или если вы настраиваете другой объект для передачи в патч с помощью new   -  person progmatico    schedule 19.04.2019
comment
Я редактирую свой ответ.   -  person progmatico    schedule 19.04.2019
comment
Хотя мой ответ работает в обеих формах, я не могу воспроизвести ваше точное сообщение об ошибке, неправильно изменив параметры patch и def. Возможно ли, что вы просто забыли поставить self в метод класса на мгновение?   -  person progmatico    schedule 19.04.2019


Ответы (1)


Если я понимаю, что вы пытаетесь сделать, это даст вам представление о том, как это сделать.

Я заставил settings.py содержать только назначение False на TARGET_SCORES_PER_SECTION. Упоминание __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()

Изменить:

Как я думаю, чтобы лучше понять вопрос ОП, приведенный выше код также можно изменить на это, если хотите:

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

Где True — параметр new. Этот объект используется для исправления того места, которое вы упомянули в строке исправления. Это может быть более элегантно, но требует, чтобы у вас был сконфигурированный объект для имитации под рукой.

person progmatico    schedule 19.04.2019
comment
Ваше решение работает, но это обходной путь. Мой вопрос об использовании mock.patch в качестве декоратора в соответствии с документами с переданным аргументом new. Передача макета в качестве аргумента функции и настройка его в функции мне не нравятся. Смотрите мое обновление вопроса. - person warvariuc; 19.04.2019
comment
@warvariuc, но это сделал не я (не мой выбор). Это функция декоратора. Декоратор изменяет сигнатуру вашей тестовой функции, чтобы добавить макет в качестве параметра. Вы даже можете складывать декораторы, и они добавят все моки в подпись. Даже если вы не используете их внутри, они должны быть прописаны в деф. Вот почему вы получаете сообщение об ошибке self. - person progmatico; 19.04.2019
comment
Вы также можете украсить класс. Это изменит все сигнатуры методов тестирования. - person progmatico; 19.04.2019
comment
На самом деле ЭТО выбор, извините. Из документов, если patch() используется в качестве декоратора, а new опущен, созданный макет передается в качестве дополнительного аргумента декорированной функции. Если patch() используется в качестве диспетчера контекста, созданный макет возвращается диспетчером контекста. - person progmatico; 19.04.2019
comment
Я нашел проблему. Это тест-раннер Nose2. Когда я хочу протестировать один метод TestCase (./manage.sh runtests -r -v tests.test_user.Test.test_register_user), он передает функцию, не связанную с self. Пока не разобрался почему так и как это исправить, но это уже другой вопрос. mock.patch работает корректно. Спасибо за ваше время. - person warvariuc; 19.04.2019