2010-06-24 2 views
5

Mein Test-Framework basiert derzeit auf einem Test-Runner-Dienstprogramm, das wiederum aus dem Eclipse pydev python abgeleitet ist Testläufer. Ich wechsle, um Nose zu verwenden, das viele Features meines benutzerdefinierten Testlaufers hat, aber besserer Code zu sein scheint.Ist es möglich, dass Nose nur Tests ausführt, die Unterklassen von TestCase oder TestSuite sind (wie unittest.main())

Meine Test-Suite enthält eine Reihe von abstrakten Test-Klassen, die vorher nie ausgeführt wurden. Der Standard-Python-Tester (und mein eigener) führte nur Instanzen von unittest.TestCase und unittest.TestSuite aus.

Ich habe festgestellt, dass seit dem Wechsel zu Nose alles läuft, was mit dem Namen "test" beginnt, was nervig ist ... weil die Namenskonvention, die wir für die Test-Mixins verwendet haben, auch aussieht wie eine Testklasse Nase. Zuvor wurden diese nie als Tests ausgeführt, da sie keine Instanzen von TestCase oder TestSuite waren.

Offensichtlich könnte ich die Methoden umbenennen, um das Wort "test" von ihren Namen auszuschließen ... das würde eine Weile dauern, weil das Testframework sehr groß ist und viel Vererbung hat. Auf der anderen Seite wäre es nett, wenn es einen Weg gäbe, Nose nur Testfälle und TestSuites als lauffähig zu sehen ... und sonst nichts.

Kann dies getan werden?

Antwort

5

Sie könnten versuchen, mit -m Option für nosetests zu spielen. Aus der Dokumentation:

Eine Testklasse ist eine Klasse in einem Testmodul definiert, das Testspiel oder ist eine Unterklasse von unittest.TestCase

-m Sätze, dass testMatch passt, auf diese Weise Sie Tests deaktivieren können alles beginnend mit test.

Eine andere Sache ist, dass Sie __test__ = False zu Ihrer Testfallklassendeklaration hinzufügen können, um es "kein Test" zu markieren.

+0

Der '__test__ = false' Trick im Falle einer übergeordneten Klasse nicht sehr nützlich ist, würde diese Kinder Klassen zwingen explizit angeben' __test__ = true' oder es würde ignoriert werden, das ist ein gefährlicher Trick zu benutzen. – Guibod

0

Sie können das Argument --attr der nose verwenden, um ein Attribut anzugeben, das vom unittest.TestCase bereitgestellt wird. Zum Beispiel verwende ich:

nosetests -A "assertAlmostEqual or addTest" 

unittest's documentation für eine vollständige Liste der Methoden/Attribute und Nase Siehe:

nosetests --attr="assertAlmostEqual" 

Sie noch vorsichtiger durch die Verwendung und und oder Matching bekommen könnten description of the capabilities of the --attr plugin.

2

Wenn Sie eine wirklich abstrakte Testklasse wünschen, könnten Sie einfach die abstrakte Klasse vom Objekt erben und später in den Testfällen erben.

Zum Beispiel:

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

Und dann verwenden Sie es mit:

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

Dann nosetests werden diejenigen, nur die Testfälle in TestMyFooFunc und nicht in AbstractTestCases abholen.

+0

Das ist ein guter Weg, um Probleme zu vermeiden, aber der abstrakte Test ist dann in meiner IDE ziemlich unlesbar. – Guibod

0

Ein Nachtrag zu @nailxx ‚s Antwort:

Sie __test__ = False in der übergeordneten Klasse setzen konnten und dann eine Metaklasse verwenden (siehe This question mit einigen brillanten Erklärungen) es auf True zurück zu setzen, wenn Subklassen.

(Schließlich fand ich eine Entschuldigung, eine Metaklasse zu verwenden!)

Obwohl __test__ ein Doppelstrich Attribut ist, müssen wir es explizit auf True gesetzt, da nicht einstellen würde es dazu führen, Python nur zum Nachschlagen das Attribut weiter auf MRO und bewerten Sie es zu False.

Daher müssen wir bei der Klasseninstanziierung prüfen, ob eine der Elternklassen __test__ = False hat. Wenn dies der Fall ist und die aktuelle Klassendefinition __test__ nicht selbst gesetzt hat, fügen wir '__test__': True zu den Attributen dict hinzu.

Der resultierende Code sieht wie folgt aus:

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) 

Man könnte dies zu einem gewissen mehr impliziten Verhalten erweitern wie „wenn der Name mit Abstract beginnt, setzte __test__ = False automatisch“, aber ich für mich die explizite Zuordnung halten würde zur Klarheit.


Lassen Sie mich einfach Unittests einfügen, das Verhalten zu demonstrieren - und als Erinnerung daran, dass jeder die zwei Minuten dauern soll, ihren Code zu testen, nachdem eine Funktion einzuführen.

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

Sie können auch Mehrfachvererbung auf der Testfallebene und lassen Sie die Basisklasse erben von object nur verwenden. Siehe 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) 
Verwandte Themen