14

In conftest (in an autouse fixture):

monkeypatch.setattr('collector.util.download_data', lambda url:"Winning" )

In collector/util.py:

def download_data(url):
    assert False

In the_caller.py:

from collector.util import download_data
def some_function():
    download_data("blah")

When I call some_function(), I get the assert. However, if I change the_caller.py to:

import collector
def some_function():
    collector.util.download_data("blah")

then I get "Winning".

Why is this behaving differently, and how can I make the monkeypatch work for both scenarios?

EmpireJones
  • 2,936
  • 4
  • 29
  • 43

1 Answers1

17

In general, it seems that the issue relates to how imports work in python. I'm not sure that there is a good solution.

The best work-around that I have found so far is the following:

monkeypatch.setattr('collector.util.download_data.__code__', replacement_function.__code__)

This works with both import types. One limitation is that this doesn't work with closures.


This functionality can be added to the framework via:

from _pytest.monkeypatch import monkeypatch

monkeypatch.setcode = lambda self, func_str, replacement_func: \
    monkeypatch.setattr(self, func_str + ".__code__", replacement_func.__code__)

Reference: https://mail.python.org/pipermail/pytest-dev/2013-October/002386.html

anthony sottile
  • 61,815
  • 15
  • 148
  • 207
EmpireJones
  • 2,936
  • 4
  • 29
  • 43
  • 12
    I think it's worth to note that that code up there is monkeypatching a monkeypatch library. – Underyx Nov 24 '15 at 20:42
  • 2
    See [the unittest docs](https://docs.python.org/dev/library/unittest.mock.html#where-to-patch) for a good explanation of this. – Alex Aug 02 '17 at 16:27
  • 1
    Wow, I was deeply lost in an unpleasant monkeypatching rabbit hole, and this is the only thing that works. Unfortunately, understanding _why_ (it works) will need to wait for another day.. – cjauvin Apr 24 '20 at 15:49
  • It turned out to be the only viable way to make monkeypatch work. – Serhii Kushchenko Jul 19 '22 at 17:57