Я пытаюсь издеваться над несколькими компонентами служебного класса. Хотя assert_called()
подходит для одного метода, он не подходит для другого, но я уверен, что оба они вызваны. Я использую Python 3.7.3 на Windows 10.
Я сократил свой сценарий до самого необходимого. Служебный класс (util.py
):
class api:
@staticmethod
def send(data):
print("sending %s" % data)
class logger:
@staticmethod
def info(s):
print("INFO: %s" % s)
@staticmethod
def error(s):
print("ERROR: %s" % s)
Вариант модульного теста, который отлично работает
import unittest
from unittest.mock import patch
from util import api
def do_something():
api.logger.info("doing something")
api.send("some data")
@patch("util.api.logger")
class Test(unittest.TestCase):
def test_do_something(self, mock_logger):
do_something()
mock_logger.info.assert_called()
Тот, который терпит неудачу:
import unittest
from unittest.mock import patch
from util import api
def do_something():
api.logger.info("doing something")
api.send("some data")
@patch("util.api")
class Test(unittest.TestCase):
def test_do_something(self, mock_api):
do_something()
mock_api.send.assert_called()
При выполнении тестов я получаю оба print()
вывода:
INFO: doing something
sending some data
Поэтому я уверен, что оба метода вызваны.
Скорее всего, я делаю глупую глупую ошибку, потому что я действительно новичок в Python ...
Еще немного предыстории:
В моем урезанном сценарии do_something()
является просто заменой для набора функций, которые являются объектом моего теста и которые в моем реальном сценарии фактически определены в отдельном файле Python. В производственной среде он работает в контексте инфраструктуры, которая предоставляет служебный класс api. В моей тестовой среде util.py
сам по себе является макетом производственного API. Загружаемый файл py (вместо def do_something(): ...
) загружается так:
path = os.getcwd() + "<local path to py file to be tested>"
globals().update({ **runpy.run_path(path, init_globals=globals()), **globals() })
Поэтому я не могу изменить код do_something()
в тестовом сценарии.
2 ответа
Наконец-то получил его после того, как нашел этот SO вопрос - я не знал, что вы можете также исправьте отдельные функции / методы класса (не упомянутые в unittest.mock.patch reference). Так что это работает:
import unittest
from unittest.mock import patch
from util import api
def do_something():
api.logger.info("doing something")
api.send("some data")
@patch("util.api.send")
@patch("util.api.logger")
class Test(unittest.TestCase):
def test_do_something(self, mock_logger, mock_send):
do_something()
mock_send.assert_called()
mock_logger.info.assert_called()
Это было сложно.
Проблема заключается в том, что класс api
импортируется тестовым классом задолго до его исправления. Из документов по патчу:
Цель импортируется при выполнении декорированной функции, а не во время декорирования.
Поскольку вы первым делом импортируете класс api
в тестовый класс, этот парень не получает исправлений при запуске теста. Python думает, что он уже импортирован.
Обратите внимание, что это не относится к классу logger
, так как вы никогда не импортируете его. Если бы вы это сделали, первый тест тоже не прошел бы.
Самое простое решение - переместить импорт в тестовую функцию.
def do_something():
from util import api
api.logger.info("doing something")
api.send("some data")
Теперь patch
выполнит импорт, поэтому макет помещается в область теста вместо реального класса.
Похожие вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.