问题
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.
回答1:
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>
回答2:
If your module was named 'foo', then the following should work.
import foo
def patched_version():
return 'Hello'
foo.__b = patched_version
print (foo.a())
where foo.py is
def a():
return __b()
def __b():
return 'Goodbye'
回答3:
I was facing the same problem, but finally got to the solution. The problem was when using the monkey patch in the unittest.TestCase class. Here's the solution that works just fine:
If you are using Python 2, you'll need to install the "mock" library (http://www.voidspace.org.uk/python/mock/) using easy_install or some other way. This library is already bundled with Python 3.
Here's what the code looks like:
from mock import patch
class TestMyModule(TestCase):
def test_basic(self):
with patch('mymodule._b') as mock:
mock.return_value={"a" : "b"} # put here what you want the mock function to return. You can make multiple tests varying these values.
#keep the indentation. Determines the scope for the patch.
print(mymodule.a('somearg'))
Although this way apparently seems a bit less convenient compared to making a mock function where we could mimic the actual sub-function, _b(), having logic to return different values based on different arguments, but then we unnecessarily add more chances of error. In this approach we just hard-code what we want our mocked function to return and test the actual function we set out to test, that was a().
来源:https://stackoverflow.com/questions/14046623/python-monkey-patch-private-function