2013-09-30 13 views
7

Próbuję utworzyć niestandardową ramkę testową jednostki, dzieląc klasę unittest.testcase, ale wydaje się, że popełniłem błąd podczas korzystania z metody __init__.przeciążanie unittest.testcase w pythonie

Nie mogę zrozumieć, dlaczego konstruktor ComplexTest nie zostanie wywołany przed tym w BasicTest i wyjątek wydaje się być również związany z moimi konstruktorami.

Jestem całkiem nowy w Pythonie, więc jakakolwiek pomoc, jak rozwiązać ten konkretny problem lub alternatywne architektury do mojego przypadku użycia byłaby najbardziej pożądana.

Dziękujemy!

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) gdy teraz próbuje uruchomić to, zobaczę następujący Stos

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 
+0

Jedną z wielkich zalet 'super' jest to, że ** nie ** musisz wyraźnie określać nadklasę. Jak widzisz w błędach nazywasz 'object .__ init__' zamiast' TestCase .__ init__' z tym kodem. – Bakuriu

+1

Tylko w Pythonie 3 można pominąć argument klasy "super". – chepner

Odpowiedz

17

Rzeczywiście Twoja metoda init jest niepoprawna.

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

Powinno być:

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

ten wezwie __init__ od klasy macierzystej BasicTest, który jest TestCase. To samo dotyczy setUp i tearDown:

class BasicTest(unittest.TestCase): 
    ... 
    def setUp(self): 
     print('BasicTest.setUp') 
     super(BasicTest, self).setUp() 
+0

Doskonale: Wydaje mi się, że źle zrozumiałem super, ale teraz działa jak urok. Dziękuję Ci! – doberkofler

+0

Ostatni wiersz przykładu setUp powinien brzmieć: 'super (BasicTest, self) .setUp()'. Teraz wywoła metodę macierzystą tearDown, która nie jest logiczna.To samo dotyczy próbek kodu pytania. – manelvf

0

Ah super! Kto wie, dlaczego to robi. Przerwanie stosowania go w teście i zamiast jawnie wywołać klasy nadrzędnej, że chcesz coś takiego:

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

Skończysz z poniższej wyjścia:

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

OK 

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

+0

Jest to prawdą tylko wtedy, gdy nie spodziewasz się, że będzie częścią wielokrotnego dziedziczenia; w przeciwnym razie może to spowodować błędy i potencjalnie spowodować nieoczekiwane zachowanie. –

+0

To nie psuje rzeczy. Zwłaszcza, że ​​'unittest.TestCase' nie używa super. Jest to przypadek bezpośredniego wywoływania kodu, który chcesz, w kolejności, w którą chcesz go nazwać. – aychedee

+0

Nieprawdziwe, jeśli masz tę i inną klasę, która podklasuje "unittest.TestCase" zarówno jako bezpośredni rodzice tej klasy, lub w inny sposób taki, który kończy się wcześniej w mro niż innej metodzie, będzie to problematyczne. Może mniej w tym konkretnym przypadku, ale generalnie nie jest to dobry wybór (szczególnie jeśli jest to bezpośrednia podklasa obiektu). –