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.
__init__()
method isn't quite a "constructor".__new__()
and__init__()
share the function of a usual constructor. Whenever you doa = MyClass()
,MyClass.__new__()
is called, which creates and returns the object instance, on which__init__()
is called, before returning the instance to be assigned toa
(in this case). – Pufferdef method(self):
is good in python ! ¡Viva el Python! :) – Phonic