Дважды запускаемая программа Python unittest

Пытаюсь больше понять unittest.mock, но не уверен, почему он запускает программу дважды. Для простоты рассмотрим код ниже в файле test.py:

from unittest.mock import patch

class T():
    def __init__(self):
        self.setup()

    def setup(self):
        mock_testing = patch('test.testing').start()
        mock_testing.return_value = "new testing"

def testing():
    return "testing"

print("Hello")

t = T()

print("Setting up")

if testing() == "testing":
    print("old style")
elif testing() == "new testing":
    print("new style")

Когда я запускаю скрипт с python test.py, я получаю:

Hello
Hello
Setting up
new style
Setting up
old style

Почему он запускает код дважды? И даже если он запустит его дважды, почему «привет» печатается подряд, если он печатается так:

Hello
Setting up
new style
Hello
Setting up
old style

Кроме того, как я могу сделать так, чтобы он просто запускал код один раз с фиктивным значением «новое тестирование»?


person user1179317    schedule 14.11.2019    source источник
comment
Я запускаю его как python test.py.. и он дает эти результаты   -  person user1179317    schedule 14.11.2019


Ответы (1)


Это связано с тем, что скрипт сначала загружается как модуль __main__, а вы вызываете patch с test.testing, поэтому patch снова импортирует test.py как модуль test. Поскольку patch вызывается до того, как будет напечатано "Setting up", загрузка модуля test, а также печать "Hello" как модулем __main__, так и модулем test будут выполнены до того, как "Setting up" будет напечатано модулем __main__.

Если вы добавите __name__ к аргументам для print, вам будет легче увидеть, что происходит:

from unittest.mock import patch

class T():
    def __init__(self):
        self.setup()

    def setup(self):
        mock_testing = patch('test.testing').start()
        mock_testing.return_value = "new testing"

def testing():
    return "testing"

print(__name__, "Hello")

t = T()

print(__name__, "Setting up")

if testing() == "testing":
    print(__name__, "old style")
elif testing() == "new testing":
    print(__name__, "new style")

Это выводит:

__main__ Hello
test Hello
test Setting up
test new style
__main__ Setting up
__main__ old style

Чтобы избежать этого, вы должны вместо этого пропатчить __main__.testing, чтобы после модификации выводился приведенный выше код:

__main__ Hello
__main__ Setting up
__main__ new style
person blhsing    schedule 14.11.2019
comment
Это имеет большой смысл. Не понял, что патч снова импортировал модуль. Большое спасибо за объяснение! - person user1179317; 14.11.2019
comment
Импорт одного и того же модуля с двумя разными именами настоятельно не рекомендуется и может привести к множеству странных ошибок. На самом деле код импортируется дважды, глобальные переменные существуют в двух разных пространствах имен с потенциально двумя разными значениями. patch импортирует модуль снова, только если вы импортируете его с другим именем. Другими словами, если вы выполняете скрипт как __main__, то вам не следует исправлять "test.testing", вместо этого вы должны издеваться над "__main__.testing". - person gelonida; 17.11.2019