Question

The key to this question is aiding unit-testing. If I have a busy __init__ (i.e. __init__ that does complex initialization), I cannot simply instantiate an object of a class, but I need to mock/stub out all methods invoked on dependencies within the __init__.

To illustrate this problem, here is example:

class SomeClass(object):
    def __init__(self, dep1, dep2, some_string):
        self._dep1 = dep1
        self._dep2 = dep2
        self._some_string = some_string

        # I would need to mock everything here (imagine some even more
        # complicated example)
        for dep2element in self._dep2:
            dep2element.set_dep(dep1)
        self._dep1.set_some_string(some_string)

    def fun1(self):
        ...
    def fun2(self):
        ...
    def fun3(self):
        ...

To test the fun* functions, every test must perform the complex construction.

class TestSomeClass(TestCase):
    def create_SomeClass(self, some_string):
        dep1 = Mock()
        # mock everything required by SomeClass' constructor

        dep2 = Mock()
        # mock everything required by SomeClass' constructor

        return SomeClass(dep1, dep2, some_string)

    def test_fun1(self):
        sc = self.create_SomeClass('some string')
        ...

    def test_fun2(self):
        sc = self.create_SomeClass('some other string')
        ...

    def test_fun3(self):
        sc = self.create_SomeClass('yet another string')
        ...

I find this redundant and would like to know how this problems can be elegantly handled in python, if not by moving the work from the constructor.

SOLUTION:

As @ecatmur suggested, to test some specific function, this code should do the trick:

def test_some_method():
    mobject = Mock(SomeClass)
    SomeClass.fun1(mobject)

With this approach all the methods will be mocked out. If fun1 calls some other method you want executed (e.g. fun2) you can do it like this:

def test_some_method():
    mobject = Mock(SomeClass)
    mobject.fun2 = SomeClass.fun2.__get__(mobject)
    SomeClass.fun1(mobject)

SomeClass.fun2.__get__(mobject) will produce instancemethod which will provide the correct binding.

¡Viva el Python!

ORIGINAL QUESTION:

Original question was centered around moving the work done in __init__ to the separate init method and different problems revolving that approach. My usual approach is to make this

class SomeClass(object):
    def __init__(self, dep1, dep2, some_string)
        self._dep1 = dep1
        self._dep2 = dep2

        # lots of mumbo-jumbo here...

become this

class SomeClass(object):
    def __init__(self, dep1, dep2)
        self._dep1 = dep1
        self._dep2 = dep2

    def initiate(self, some-string)
        # lots of mumto-jumbo here...

General sentiment was that moving work from __init__ is not a common practice and would be meaningless to seasoned python developers.

Was it helpful?

Solution

If you write initialization functions separate from __init__ then experienced developers will certainly see your code as a kid's playground.

If you're concerned about being able to create objects that look like instances of your class without running the __init__ method, then use Mock:

def test_some_method():
    mock_object = Mock(MyClass)
    MyClass.some_method(mock_object)

OTHER TIPS

__init__ can't actually return anything; if you think about how it is used, this should be obvious:

class Example(object):
    def __init__(self, x):
        self.x = x
        return ANYTHING

e = Example(1)   # how are you going to get ANYTHING back?

Using an initialize() method, separate from __init__, seems kind of silly - the whole point is that your initializer should automatically run when the object is created, so your example initialization should look like

scobj = SomeClass(dep1, dep2, 'abcdef')
# scobj.initialize('abcdef')     # <= separate call not needed!

Edit:

If you really need to factor the code in __init__, I suggest putting it in private methods and calling those from __init__, like so:

class Example2(object):
    def __init__(self, a, b, c):
        self._init_connection(a)
        self._init_display(b)
        self.c = c

    def _init_connection(self, a):
        self.conn = make_connection(a)

    def _init_display(self, b):
        self.disp = make_display(b)

... this is good because

  • all initialization is done on creating the object (you don't need to remember to make follow-up initialization calls)
  • you don't have to keep a flag to remember whether your object has been initialized or not - if it exists, it has been initialized.
class Klass:
    def __init__(self):
        initialize instance here, no return

__init__ will automatically run at object creation, so you don't need to check if it run; it was run if you have an instance!

About returning self from init(): I wouldn't, as it is an in-place operation. Read the first answer here, as it explains this pretty well.

What about

class SomeClass(object):
    def __init__(self, dep1, dep2, st=None):
        self._dep1 = dep1
        self._dep2 = dep2
        if st is None:
            self._initialized = False
        else:
            self.initialize(st)

    def initialize(self, st):
        ...
        self._initialized = True
        ...

?

There's nothing wrong with splitting __init__() out into separate methods. If you can do it in a way which makes those methods appropriately named and reusable, then all the better - e.g.:

class SomeClass(object):
    def __init__(self, dep1, dep2, some_string):
        self.set_dependencies(dep1, dep2, some_string)

    def set_dependencies(self, dep1, dep2, some_string):
        self._dep1 = dep1
        self._dep2 = dep2
        self._some_string = some_string

        for dep2element in self._dep2:
            dep2element.set_dep(dep1)
        self._dep1.set_some_string(some_string)

Here, a remove_dependencies() method could be added in the future to revert the instance to a neutral state, before setting new dependencies. The functionality is decoupled from the initialisation process.

Note I've called this from __init__(), even though we don't want this to run in testing. If we don't call this from __init__(), someone using this class would have to call set_dependencies() after instantiating - which is an unnecessary complication to your class's API.

What we can do is stub the method out for testing, e.g.:

class TestSomeClass(TestCase):
    def __init__(self):
        SomeClass._old_set_dependencies = SomeClass.set_dependencies
        SomeClass.set_dependencies = lambda *args: None

    ...

This is a pretty brash way of stubbing out a method, just to prove a point really - there are some good libraries out there for doing it more diligently - see this discussion, but I'd also recommend Mockstar which is an extension to Mock designed to simplify the semantics.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top