2015-11-03 26 views
5

Kiedy zachodzi znaczące nakładanie się w konfiguracji testu, może sprawić, że rzeczy DRY będą używać dziedziczenia. Ale to powoduje problemy z niepotrzebnego powielania wykonanie testu:Dlaczego testy w klasach pochodnych powodują ponowne uruchomienie testów klasy nadrzędnej?

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' 

Testowanie ten moduł uruchamia test_in_parent dwukrotnie.

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

OK 

Dlaczego? Czy to według projektu? Czy można go wyłączyć, konfigurując program testowy w określony sposób?

Mogę obejść ten problem, przenosząc program instalacyjny na nieodkrytą klasę, a następnie korzystam z wielu dziedziczeń, ale wydaje się on nieco hacky i niepotrzebny.

uwaga: sam problem występuje w innych biegaczy, takich jak nos (nosetests -s example.py) i pytest (py.test example.py)

+2

Bo oni dziedziczą metody badań swoich rodziców, więc gdy 'unittest' przegląda ich' dir' (lub '__dict__' czy cokolwiek to robi) dla metod rozpoczynających 'test_' znajduje również odziedziczone. Nie sądzę, że rozwiązanie tego wymaga * wiele * dziedziczenia; streszczenie, co do trzeciej, nieodwracalnej klasy, bez metod "testowych", i obaj dziedziczą ją. – jonrsharpe

+0

Introspekcja podklasy posiadającej klasę nadrzędną, która zawiera metody poprzedzone przez test, pokaże podklasę tymi metodami. To jest Python OOP. Nie sądzę, aby przenoszenie metod konfiguracji do mixin lub jako oddzielnej klasy bazowej w ogóle wydawało się być hackowate i być może DRYER – dm03514

+0

Wydaje się, że to świetny przypadek użycia dla mixinów dla różnych "grup" testów, aby dynamicznie stworzyć dokładny test, którego potrzebujesz. ..tree-dziedziczenie nie wygląda tutaj jak właściwy model. – Shashank

Odpowiedz

3

biegaczy testowe patrzeć w górę wszystkich metod rozpoczynających się test. Metody odziedziczone są obecne w klasie potomnej - dlatego są wykrywane jako testy do uruchomienia. Aby tego uniknąć, należy wyodrębnić wspólny kod w klasie nadrzędnej i nie dziedziczyć żadnych rzeczywistych testów.

+0

Tak, jestem świadomy tego obejścia i użyłem go w przeszłości. Ale nie podoba mi się to! – wim

+0

Osiedliłem się na schemacie podobnym do tego - zamiast 'PotatoTestTemplate' używam' PotatoSetup (object) '. To nie jest 'TestCase', bardziej przypomina mixin. Następnie używam 'PotatoTest (PotatoSetup, TestCase)' i 'SpudTest (PotatoSetup, TestCase)' z dodatkowymi ustawieniami w każdym teście, jeśli go potrzebuję. – wim

1

Innym obejściem, którego używają ludzie, jest to, że zagnieżdżone klasy nie są uruchamiane jako część testów nosa, np.

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' 

Rozwiązaniem bezskutecznie próbowała było zastosowanie wielu dziedziczenie więc klasa TestPotato rozciąga obiekt i TestSpud nie wystają z TestCase i TestPotato np

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' 

Ale to faktycznie nie działa dla mnie, żeby to zrobił, bo nie musiałby dodatkową zagnieżdżania kodu ... ale wygląda na to, używając multiple inheritance is bad anyway

1

Jeśli konfiguracja testu z inna klasa test jest wszystko, co trzeba, można to zrobić:

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'