Mocking complicated __init__ in Python

George Shuklin
2 min readDec 2, 2016

--

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 None
def 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.

--

--

George Shuklin

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.