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