2010-06-24 14 views
5

Moje środowisko testowe jest obecnie oparte na narzędziu test-runner, które samo pochodzi z pejcza Eclipse pydev test-runner. Przełączam się, aby używać Nosa, który ma wiele cech mojego niestandardowego testu-biegacza, ale wydaje się być lepszej jakości kod.Czy możliwe jest wykonywanie testów Nose tylko testów, które są podklasami TestCase lub TestSuite (jak unittest.main())

Mój zestaw testów zawiera wiele abstrakcyjnych testów, które wcześniej nigdy nie działały. Standardowy testhon testujący Pythona (i mój niestandardowy) uruchamiał tylko instancje unittest.TestCase i unittest.TestSuite.

Zauważyłem, że odkąd przełączyłem się na Nose, działa prawie wszystko, co zaczyna się od nazwy "test", co jest denerwujące ... ponieważ konwencja nazewnicza, której używaliśmy do test-mixin również wygląda jak klasa testowa Nos. Poprzednio te nigdy nie działały jako testy, ponieważ nie były instancjami TestCase ani TestSuite.

Oczywiście mogłem zmienić nazwę metod, aby wykluczyć słowo "test" z ich nazwisk ... wymagałoby to trochę czasu, ponieważ struktura testowa jest bardzo duża i ma wiele dziedziczenia. Z drugiej strony byłoby fajnie, gdyby istniał sposób, aby Nose widział tylko TestCases i TestSuites jako działające ... i nic więcej.

Czy to można zrobić?

Odpowiedz

5

Możesz spróbować grać z opcją -m dla . Z dokumentacji:

Klasa testu jest klasa zdefiniowana w module testowego, który odpowiada testMatch lub jest podklasą unittest.TestCase

-m zestawy, które testMatch, w ten sposób można wyłączyć testowanie wszystko zaczynające się od testu.

Kolejną rzeczą jest to, że można dodać __test__ = False do deklaracji klasy przypadków testowych, aby oznaczyć ją jako "nie jest to test".

+0

Opcja '__test__ = FALSE Sztuką nie jest bardzo przydatna w przypadku klasy nadrzędnej, to ta siła dzieciom zajęcia jednoznacznego określenia' __test__ = TRUE lub byłoby ignorowane, że to niebezpieczne trik do użycia. – Guibod

0

Możesz użyć argumentu nosa --attr, aby określić atrybut posiadany przez unittest.TestCase. Na przykład, używam:

nosetests --attr="assertAlmostEqual" 

Można dostać nawet bardziej ostrożny używając i i lub dopasowanie:

nosetests -A "assertAlmostEqual or addTest" 

Zobacz unittest's documentation aby uzyskać pełną listę metod/atrybuty i nos użytkownika description of the capabilities of the --attr plugin.

2

Jeśli chcesz naprawdę abstrakcyjną klasę testową, możesz po prostu dziedziczyć klasę abstrakcyjną z obiektu, a następnie dziedziczyć później w testach.

Na przykład:

class AbstractTestcases(object): 
    def test_my_function_does_something(self): 
     self.assertEquals("bar", self.func()) 

a następnie użyć go z:

class TestMyFooFunc(AbstractTestcases, unittest.TestCase): 
    def setUp(self): 
     self.func = lambda: "foo" 

Następnie nosetests wzrośnie tylko testami w TestMyFooFunc a nie tych w AbstractTestCases.

+0

To dobry sposób na uniknięcie problemu, ale abstrakcyjny test jest wówczas dość nieczytelny w moim IDE. – Guibod

0

Jeden dodatek do odpowiedzi @nailxx „s:

Można ustawić __test__ = False w klasie macierzystej, a następnie użyć metaklasa (patrz This question z niektórych genialnych wyjaśnień), aby ustawić go na wartość True, gdy instacji.

(Wreszcie znalazłem pretekst do korzystania z metaklasa!)

Chociaż __test__ jest dwukrotnie atrybut podkreślenia, musimy jawnie ustawić go True, ponieważ nie ustawienie spowodowałoby to python tylko do wyszukiwania atrybut dalej do MRO i oceń go na False.

W związku z tym musimy sprawdzić w klasie wystąpienie, czy jedna z klas nadrzędnych ma __test__ = False. Jeśli tak jest, a obecna definicja klasy nie ustawi sama w sobie , dodamy '__test__': True do atrybutu dict.

Otrzymany kod wygląda następująco:

class TestWhenSubclassedMeta(type): 
    """Metaclass that sets `__test__` back to `True` when subclassed. 

    Usage: 

     >>> class GenericTestCase(TestCase, metaclass=TestWhenSubclassed): 
     ...  __test__ = False 
     ... 
     ...  def test_something(self): 
     ...   self.fail("This test is executed in a subclass, only.") 
     ... 
     ... 
     >>> class SpecificTestCase(GenericTestCase): 
     ...  pass 

    """ 

    def __new__(mcs, name, bases, attrs): 
     ATTR_NAME = '__test__' 
     VALUE_TO_RESET = False 
     RESET_VALUE = True 

     values = [getattr(base, ATTR_NAME) for base in bases 
        if hasattr(base, ATTR_NAME)] 

     # only reset if the first attribute is `VALUE_TO_RESET` 
     try: 
      first_value = values[0] 
     except IndexError: 
      pass 
     else: 
      if first_value == VALUE_TO_RESET and ATTR_NAME not in attrs: 
       attrs[ATTR_NAME] = RESET_VALUE 

     return super().__new__(mcs, name, bases, attrs) 

Można by rozszerzyć to do jakiegoś bardziej niejawnego zachowania jak „jeśli nazwa zaczyna się Abstract ustaw __test__ = False automatycznie”, ale za to sam utrzymywać wyraźne przypisanie dla jasności.


Pozwól wklej proste unittests celu wykazania zachowanie - i jako przypomnienie, że każdy powinien podjąć dwie minuty, aby przetestować swój kod po wprowadzeniu funkcji.

from unittest import TestCase 

from .base import TestWhenSubclassedMeta 


class SubclassesTestCase(TestCase): 
    def test_subclass_resetted(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      __test__ = False 

     class C(Base): 
      pass 

     self.assertTrue(C.__test__) 
     self.assertIn('__test__', C.__dict__) 

    def test_subclass_not_resetted(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      __test__ = True 

     class C(Base): 
      pass 

     self.assertTrue(C.__test__) 
     self.assertNotIn('__test__', C.__dict__) 

    def test_subclass_attr_not_set(self): 
     class Base(metaclass=TestWhenSubclassedMeta): 
      pass 

     class C(Base): 
      pass 

     with self.assertRaises(AttributeError): 
      getattr(C, '__test__') 
0

Można również użyć wielokrotne dziedziczenie na poziomie przypadku testowego i niech Dziedziczy klasy bazowej tylko z object. Zobacz this thread:

class MyBase(object): 
    def finishsetup(self): 
     self.z=self.x+self.y 

    def test1(self): 
     self.assertEqual(self.z, self.x+self.y) 

    def test2(self): 
     self.assert_(self.x > self.y) 

class RealCase1(MyBase, unittest.TestCase): 
    def setUp(self): 
     self.x=10 
     self.y=5 
     MyBase.finishsetup(self) 

class RealCase2(MyBase, unittest.TestCase): 
    def setUp(self): 
     self.x=42 
     self.y=13 
     MyBase.finishsetup(self) 
Powiązane problemy