2015-11-03 19 views
5

Wenn die Testkonfiguration stark überlappt, kann DRY die Vererbung beibehalten. Aber dies führt zu Problemen mit unnötiger Doppeltestausführung:Warum führen Tests in abgeleiteten Klassen die Elternklasse-Tests erneut aus?

from unittest import TestCase 

class TestPotato(TestCase): 
    def test_in_parent(self): 
     print 'in parent' 

class TestSpud(TestPotato): 
    def test_in_child(self): 
     print 'in child' 

Prüfung dieses Modul der test_in_parent zweimal ausgeführt wird.

$ python -m unittest example 
in parent 
.in child 
.in parent 
. 
---------------------------------------------------------------------- 
Ran 3 tests in 0.000s 

OK 

Warum? Ist das Absicht? Kann es deaktiviert werden, indem der Test Runner auf eine bestimmte Art konfiguriert wird?

Ich kann das Problem umgehen, indem Sie Setup in eine nicht entdeckte Klasse verschieben, und dann mehrere Erben verwenden, aber es scheint ein bisschen hacky und unnötig.

Anmerkung: Das gleiche Problem tritt bei anderen Läufern wie Nase (nosetests -s example.py) und pytest (py.test example.py)

+2

Weil sie die Testmethoden ihrer Eltern erben, also wenn 'unittest' durch ihre' dir' (oder '__dict__' aussieht oder was auch immer es tut) Für Methoden, die 'test_' starten, findet man auch die ererbten. Ich glaube nicht, dass das Lösen * multiple * Vererbung erfordert; abstract was beide zu einer dritten, nicht zu entdeckenden Klasse ohne 'test_' Methoden benötigen und sie beide erben lassen. – jonrsharpe

+0

Das Einsehen einer Unterklasse mit einer Elternklasse, die Methoden enthält, denen test vorangestellt ist, zeigt die Unterklasse mit diesen Methoden an. Dies ist Python OOP. Ich denke nicht, dass das Verschieben von Setup-Methoden zum Mixen oder als separate Basisklasse überhaupt hacky ist und vielleicht DRYer – dm03514

+0

Scheint wie ein großartiger Anwendungsfall für Mixins für verschiedene "Gruppen" von Tests, um den genauen Test dynamisch zu erstellen. ..Tree-Vererbung sieht hier nicht wie das richtige Modell aus. – Shashank

Antwort

3

Test-Läufer aller Methoden mit test Start nachschlagen. Inherited-Methoden sind in der Unterklasse vorhanden - daher werden sie als auszuführende Tests erkannt. Um dies zu vermeiden, sollten Sie allgemeinen Code in der übergeordneten Klasse extrahieren und keine tatsächlichen Tests erben.

+0

Ja, ich kenne diese Problemumgehung und habe sie in der Vergangenheit verwendet. Aber ich mag es nicht! – wim

+0

Ich habe mich für ein ähnliches Muster entschieden - anstelle des 'PotatoTestTemplate' verwende ich ein' PotatoSetup (Objekt) '. Es ist kein 'TestCase', es ist mehr wie ein Mixin. Dann benutze ich 'PotatoTest (PotatoSetup, TestCase)' und 'SpudTest (PotatoSetup, TestCase)' mit extra Setup in beiden Tests, wenn ich es brauche. – wim

1

Eine andere Problemumgehung, die ich gesehen habe, ist, dass verschachtelte Klassen nicht als Teil von Nosetests ausgeführt werden, z.

from unittest import TestCase 
class NotTested: 
    class TestPotato(TestCase): 
     def test_in_parent(self): 
      print 'in parent' 

class TestSpud(NotTested.TestPotato): 
    def test_in_child(self): 
     print 'in child' 

Eine Abhilfe I erfolglos versucht, die Mehrfachvererbung zu verwenden war, so dass die TestPotato Klassenobjekt erstreckt, und erstreckt sich von TestSpud Testcase und TestPotato z.B.

from unittest import TestCase 

class TestPotato(object): 
    def test_in_parent(self): 
     # still gets ran twice because 
     # nosetests runs anything that starts with test_* :(
     print 'in parent' 

class TestSpud(TestCase, TestPotato): 
    def test_in_child(self): 
     print 'in child' 

Aber das ist eigentlich nicht für mich arbeiten, ich wünschte, es tat, weil Sie den zusätzlichen Verschachtelung der Code nicht brauchen würde ... aber es sieht aus wie multiple inheritance is bad anyway

1

Wenn der Test Installation und Konfiguration mit von eine andere Testklasse ist alles, was Sie benötigen, können Sie dies tun:

from unittest import TestCase 

class TestPotato(TestCase): 
    def setUp(self): 
     print('fixtures here') 

    def test_in_parent(self): 
     print 'in parent' 


class TestSpud(TestCase): 
    def setUp(self): 
     TestPotato.setUp(self) 

    def test_in_child(self): 
     print 'in child'