Glueon Glueon - 3 months ago 14
Python Question

Pytest monkeypatch isn't working on imported function

Suppose there are two packages in a project:

some_package
and
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 ...


I want to test
another_package.function.call_bar
mocking out
some_package.foo.bar
because it has some network I/O I want to avoid.

Here is a test:

# 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


To my surprise it outputs
hello
instead of
mock
. I tried to debug this thing putting ipdb breakpoint in the test. When I manually import
some_package.foo.bar
after breakpoint and call
bar()
I get
patched
.

On my real project the situation is even more interesting. If I invoke pytest in the project root my function isn't patched, but when I specify
tests/test_bar.py
as an argument - it works.

As far as I understand it has something to do with the
from some_package.foo import bar
statement. If it's being executed before monkeypatching is happening then it patching fails. But on the condensed test setup from the example above patching does not work in both cases.

And why does it work in IPDB REPL after hitting a breakpoint?

Answer

named importation creates a new name for the object, if you then replace the old name for the object the new name is unaffected

import the module and use module.bar instead, that will always use the current object

edit:

import module 

def func_under_test():
  module.foo()


def test_func():
   monkeypatch.setattr(...)
   func_under_test
Comments