Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

py.test's monkeypatch.setattr(...) not working in some cases

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?

like image 398
EmpireJones Avatar asked Feb 09 '15 05:02

EmpireJones


People also ask

What is monkeypatch setattr?

monkeypatch can be used to patch functions dependent on the user to always return a specific value. In this example, monkeypatch. setattr is used to patch Path. home so that the known testing path Path("/abc") is always used when the test is run. This removes any dependency on the running user for testing purposes.

How does monkeypatch work?

monkeypatch is a part of the pytest-mock library that allows you to intercept what a function would normally do, substituting its full execution with a return value of your own specification. Note that monkey patching a function call does not count as actually testing that function call!

Why do we need monkey patching?

Monkey patching is a technique used to dynamically update the behavior of a piece of code at run-time. A monkey patch (also spelled monkey-patch, MonkeyPatch) is a way to extend or modify the runtime code of dynamic languages (e.g. Smalltalk, JavaScript, Objective-C, Ruby, Perl, Python, Groovy, etc.)

What is monkey patching how do you use it in Python?

In Python, the term monkey patch refers to dynamic (or run-time) modifications of a class or module. In Python, we can actually change the behavior of code at run-time. We use above module (monk) in below code and change behavior of func() at run-time by assigning different value.


1 Answers

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

like image 131
EmpireJones Avatar answered Nov 03 '22 10:11

EmpireJones