Mocking complicated __init__ in Python
my small discovery of a day
Do you know you can mock __init__
of classes you are testing?
Let’s say you have nasty __init__()
in your class and you want to test some simple method of that same class.
class Compl(object):
def __init__(self, arg1, arg2):
self.var1 = complicated_to_mock(arg1)
self.var2 = self.var2.complicated_to_mocK2(arg2)
if self.var1 in self.var2 and self.var1 != self.var2:
self.var3 = self.var2 - self.var1 + ctm3(ctm4())
def simple(self, arg3):
if arg3 > self.var1:
return None
else:
return arg3 - self.var1
sorry for nonsense, I just wanted to show a nasty __init__().
Now you want to test simple()
function. And you don’t want to mock all that mad complicated_to_mock
function with complicated relationships between return values and their properties.
My new discovery: you can mock __init__()
method of any class. __init__
function should have two important properties: it should take at least one arg (self) and return None
.
Here is a simple test for our simple simple()
in our complicated Compl
:
from mock import patchdef test_simple_none():
with patch.object(Compl, "__init__", lambda x, y, z: None):
c = Compl(None, None)
c.var1 = 0
assert c.simple(1) is Nonedef test_simple_substraction():
with patch.object(Compl, "__init__", lambda x, y, z: None):
c = Compl(None, None)
c.var1 = 1
assert c.simple(1) == 0
or, if you are really in pytest, you can paramerized it:
from mock import patch
import pytest@pytest.mark.parametrize("input, output", [
[0, None],
[1, 0]
])
def test_simple(input, output):
with patch.object(Compl, "__init__", lambda x, y, z: None):
c = Compl(None, None)
c.var1 = input
assert c.simple(1) == output
It’s simple and elegant, and very self-describing. No more long staircase of patches to satisfy __init__’s.
Lambda?
Some people was curious why there is a lambda in a patch statement. It’s simple: lambda return us an anonymous function. According to our specs it takes three agruments and returns None
. If you look to the signature of __init__
, it too takes three arguments and return None
. So we use lambda to create a simple __init__
substitute (looks like __init__
, do nothing), which we use to mock-patch the class.