问题
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