14

I'm trying to create a custom unit test framework by sub-classing the unittest.testcase class but seem to make a mistake when dealing with the __init__ method.

I cannot figure out why the constructor of ComplexTest doesn't get invoked before the one in BasicTest and the exception also seems to be related to my constructors.

I'm pretty new to Python so any help on how to solve this specific problem or alternative architectures to my use case would be most welcome.

Thank you!

1) test_framework.py

import unittest

class BasicTest(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        print('BasicTest.__init__')
        super(unittest.TestCase, self).__init__(*args, **kwargs)

    def setUp(self):
        print('BasicTest.setUp')
        super(unittest.TestCase, self).tearDown()

    def tearDown(self):
        print('BasicTest.tearDown')
        super(unittest.TestCase, self).tearDown()


class ComplexTest(BasicTest):
    def __init__(self, *args, **kwargs):
        print('ComplexTest.__init__')
        super(BasicTest, self).__init__(*args, **kwargs)

    def setUp(self):
        print('ComplexTest.setUp')
        super(BasicTest, self).tearDown()

    def tearDown(self):
        print('ComplexTest.tearDown')
        super(BasicTest, self).tearDown()

2) test.py

import unittest
import test_framework

class TestValueFunctions(test_framework.ComplexTest):
    def __init__(self, *args, **kwargs):
        print('TestValueFunctions.__init__')
        super(test_framework.ComplexTest, self).__init__(*args, **kwargs)

    def setUp(self):
        print('TestValueFunctions.setUp')
        super(test_framework.ComplexTest, self).tearDown()
        self.value = 4711

    def tearDown(self):
        print('TestValueFunctions.tearDown')
        super(test_framework.ComplexTest, self).tearDown()

    def test_value(self):
        print('TestValueFunctions.test_value')
        self.assertEqual(self.value, 4711)

if __name__ == '__main__':
    unittest.main()

3) when now trying to run this, i see the following stack

TestValueFunctions.__init__
BasicTest.__init__
Traceback (most recent call last):
  File "D:\MyDev\ljs_app\trunk\examples\python\unittest\test.py", line 23, in <module>
    unittest.main()
  File "C:\Python27\lib\unittest\main.py", line 94, in __init__
    self.parseArgs(argv)
  File "C:\Python27\lib\unittest\main.py", line 149, in parseArgs
    self.createTests()
  File "C:\Python27\lib\unittest\main.py", line 155, in createTests
    self.test = self.testLoader.loadTestsFromModule(self.module)
  File "C:\Python27\lib\unittest\loader.py", line 65, in loadTestsFromModule
    tests.append(self.loadTestsFromTestCase(obj))
  File "C:\Python27\lib\unittest\loader.py", line 56, in loadTestsFromTestCase
    loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
  File "D:\MyDev\ljs_app\trunk\examples\python\unittest\test.py", line 7, in __init__
    super(test_framework.ComplexTest, self).__init__(*args, **kwargs)
  File "D:\MyDev\ljs_app\trunk\examples\python\unittest\test_framework.py", line 6, in __init__
    super(unittest.TestCase, self).__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters
doberkofler
  • 9,511
  • 18
  • 74
  • 126
  • One of the big advantages of `super` is that you do **not** have to explicitly state the superclass. As you see in the errors you are calling `object.__init__` instead of `TestCase.__init__` with that code. – Bakuriu Sep 30 '13 at 19:54
  • 1
    Only in Python 3 can you omit the class argument to `super`. – chepner Sep 30 '13 at 20:11

2 Answers2

31

Indeed your init method is wrong.

class BasicTest(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        print('BasicTest.__init__')
        super(unittest.TestCase, self).__init__(*args, **kwargs)

Should be:

class BasicTest(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        print('BasicTest.__init__')
        super(BasicTest, self).__init__(*args, **kwargs)

This will call __init__ on the mother class of BasicTest, which is TestCase. This is the same for setUp and tearDown:

class BasicTest(unittest.TestCase):
    ...
    def setUp(self):
        print('BasicTest.setUp')
        super(BasicTest, self).setUp()
Jonas Schäfer
  • 20,140
  • 5
  • 55
  • 69
GL770
  • 2,910
  • 1
  • 14
  • 9
  • Excellent: I've seem to have misunderstood super but now it works like a charm. Thank You! – doberkofler Oct 04 '13 at 14:03
  • last line of setUp example should be: `super(BasicTest, self).setUp()`. Right now it will call tearDown parent method, which is not logic. The same for the question code samples. – manelvf Feb 11 '14 at 19:09
2

Ah super! Who knows why it does anything. If you stop using it in your test and instead explicitly call the parent class that you want to, like this:

class BasicTest(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        print('BasicTest.__init__')
        unittest.TestCase.__init__(self, *args, **kwargs)

You'll end up with the output below:

TestValueFunctions.__init__
ComplexTest.__init__
TestValueFunctions.setUp
ComplexTest.setUp
TestValueFunctions.test_value
TestValueFunctions.tearDown
ComplexTest.tearDown
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

See: Python's Super is nifty, but you can't use it

aychedee
  • 24,871
  • 8
  • 79
  • 83
  • That's only true if you don't expect it to be part of multiple inheritance; otherwise, this will break things and potentially cause unexpected behavior. – Jeff Tratner Sep 30 '13 at 22:36
  • It doesn't break things. Especially since `unittest.TestCase` doesn't use super. It's a case of explicitly calling the code you want in the order that you want to call it. – aychedee Sep 30 '13 at 23:04
  • Not true, if you have this and another class that subclasses `unittest.TestCase` both as direct parents to this class, or another way such that this ends up earlier in the mro than another method, it'll be problematic. Maybe less so in this particular case, but more generally it's not a good choice (particularly if it's a direct subclass of object) – Jeff Tratner Oct 01 '13 at 01:53