When you’re writing unit tests, and your class/function under test uses another object, you most likely want replace it with
a mock object in your test so you can define it’s behavior. This is where Python standard library’s unittest.mock
library
comes in along with it’s Mock
class and patch
functions.
from mock import patch
...
with patch('package.module.Classname') as mockObj:
# do something with the mock
Tip 1: Figuring out the right namespace
The trouble is that specifying what to mock isn’t always quite straightforward - esp for someone like me who doesn’t fully grok how python looks up objects in namespaces. Struggled today with a couple of tests and finally figured it out - so going to write it it out so that I can come back to it at a later date.
Consider this - you have a.py
that’s imported into b.py
and you’re writing a test for a function in b.py
.
def myfunc():
# do something
pass
from a import myfunc
def functionToTest():
# do something
# call myfunc
myfunc()
# do other stuff
return
With the from a import x
form of import, now myfunc
has been imported into the b
namespace. We’d like to mock the
call to myfunc
in our test for functionToTest
- so you write patch('b.myfunc')
import b
def test_method():
with patch('b.myfunc') as myfuncMock:
# set up your mock
myfuncMock.return_value = 10
# call your sut.
b.functionToTest()
On the other hand, let’s say you used the import a
form of import in b.py
import a
def functionToTest():
# do something
# call myfunc
a.myfunc()
# do other stuff
return
Now in this case, the object to mock is looked up from the a
namespace - so that’s what you target in the patch
call.
import b
def test_method():
with patch('a.myfunc') as myfuncMock:
myfuncMock.return_value = 10
# call your sut.
b.functionToTest()
Tip 2: Using patch
as a decorator - argument order
Patch can be used as a function, class or method decorator. If at a class level then each method gets an extra argument which is the mock passed in. If you patch more than one object, then they’re passed in outside in:
@patch('b.myfunc') (1)
@patch('b.myotherfunc')
def someMethod(self,
otherfuncMock, (2)
myfuncMock):
# do something here...
1 | Notice the order of patch decorators |
2 | method arguments are the other way in |
Given that I have to look this up everytime, I’m pretty sure that the future me is going to like the present me for this!