I have a module with a function (call it a()
) that calls another function defined in the same module (call it __b()
). __b()
is a function which speaks to a website via urllib2
and gets some data back. Now I'm trying to test a()
, but of course would rather not have my unit tests speak to the public internet. Thus, I'm thinking if I can monkey patch __b()
with a function which returns canned data, then I can write the tests for a()
.
To be more concrete, my module looks kinda like:
def a():
return __b("someval")
def __b(args):
return something_complex_with_args
So now I want to test a()
, but I need to monkey patch out __b
. The problem is that A) the vast majority of information on monkey patching applies to methods of a class, not to functions in a module, and B) the function I want to monkey patch is private. I am willing to change __b
to be non-private if it makes the process more feasible, but would rather not.
Suggestions?
Edit: as it stands the test class looks like:
from unittest import TestCase
import mymodule
def newfn(args):
return {"a" : "b"}
mymodule._b = newfn
class TestMyModule(TestCase):
def test_basic(self):
print(mymodule.a('somearg'))
And when I run this, I see the output if the monkey patching had not been done at all, rather than seeing {'a': 'b'}
get printed out.
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.
To really "monkeypatch" that you would need to change the function itself (you are only changing what names refer to); this is possible, but you do not actually want to do that. In the first attempt, you make do_something_expensive a global name in your module.
There's also the dectools package, which I saw an PyCon 2010, which may be able to be used in this context too, but that might actually go the other way (monkeypatching at the method declarative level... where you're not) Show activity on this post.
2 I would recommend to use the most simple and shortest approach, which is in Python probably option A, monkey patching. Everything else makes the code more complicated, with no substantial benefit.
I can't seem to reproduce your issue (I've tweaked your example a bit, since it doesn't run at all as-is). Did you just mistype something (like mymodule._b
instead of mymodule.__b
)?
mymodule.py:
def a(x):
return __b("someval")
def __b(args):
return "complex_thingy: {}".format(args)
mytest.py:
from unittest import TestCase
import mymodule
def newfn(args):
return {"a" : "b"}
mymodule.__b = newfn
class TestMyModule(TestCase):
def test_basic(self):
print(mymodule.a('somearg'))
Output:
C:\TEMP>python -m unittest mytest
{'a': 'b'}
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
C:\TEMP>
Seems to work fine.
Or outside of unittest:
mytest2.py:
import mymodule
def newfn(args):
return {"a" : "b"}
mymodule.__b = newfn
print(mymodule.a('somearg'))
Output:
C:\TEMP>python mytest2.py
{'a': 'b'}
C:\TEMP>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With