Pytest monkeypatch не работает с импортированной функцией
Предположим, в проекте есть два пакета: some_package
и another_package
.
# some_package/foo.py:
def bar():
print('hello')
# another_package/function.py
from some_package.foo import bar
def call_bar():
# ... code ...
bar()
# ... code ...
Я хочу протестировать another_package.function.call_bar
с some_package.foo.bar
потому что в нем есть сетевой ввод- some_package.foo.bar
которого я хочу избежать.
Вот тест:
# tests/test_bar.py
from another_package.function import call_bar
def test_bar(monkeypatch):
monkeypatch.setattr('some_package.foo.bar', lambda: print('patched'))
call_bar()
assert True
К моему удивлению, он выводит hello
а не patched
. Я попытался отладить эту вещь, поставив точку останова IPDB в тесте. Когда я вручную импортирую some_package.foo.bar
после точки останова и вызова bar()
меня patched
.
На моем реальном проекте ситуация еще интереснее. Если я вызываю pytest в корне проекта, моя функция не исправляется, но когда я указываю в качестве аргумента tests/test_bar.py
- это работает.
Насколько я понимаю, это как-то связано с from some_package.foo import bar
. Если он выполняется до того, как происходит monkeypatching, то исправление не выполняется. Но на сжатой тестовой настройке из вышеприведенного примера установка исправлений не работает в обоих случаях.
И почему он работает в IPDB REPL после достижения точки останова?
Ответы
Ответ 1
Именованный импорт создает новое имя для объекта. Если вы затем замените старое имя для объекта, новое имя не изменится.
Импортируйте модуль и используйте module.bar
. Это всегда будет использовать текущий объект.
РЕДАКТИРОВАТЬ:
import module
def func_under_test():
module.foo()
def test_func():
monkeypatch.setattr(...)
func_under_test
Ответ 2
Пока работает ответ Ронни, он заставляет вас изменить код приложения. В общем, вы не должны делать это ради тестирования.
Вместо этого вы можете явно пропатчить объект во втором пакете. Это упоминается в документации для модуля unittest.
monkeypatch.setattr('another_package.bar', lambda: print('patched'))