Ich habe derzeit ein paar Unit-Tests, die einen gemeinsamen Testsatz haben. Hier ist ein Beispiel:
import unittest
class BaseTest(unittest.TestCase):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __== '__main__':
unittest.main()
Die Ausgabe des obigen ist:
Calling BaseTest:testCommon
.Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
Gibt es eine Möglichkeit, das obige so umzuschreiben, dass die allererste testCommon
nicht aufgerufen wird?
EDIT: Statt 5 Tests auszuführen, soll ich nur 4 Tests durchführen, 2 vom SubTest1 und 2 vom SubTest2. Es scheint, als würde Python allein den ursprünglichen BaseTest alleine ausführen, und ich brauche einen Mechanismus, um das zu verhindern.
Verwenden Sie mehrfache Vererbung, damit Ihre Klasse mit allgemeinen Tests nicht selbst von TestCase erbt.
import unittest
class CommonTests(object):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(unittest.TestCase, CommonTests):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(unittest.TestCase, CommonTests):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __== '__main__':
unittest.main()
Verwenden Sie keine Mehrfachvererbung, es wird Sie später beißen.
Stattdessen können Sie Ihre Basisklasse einfach in das separate Modul verschieben oder mit der leeren Klasse umschließen:
import unittest
class BaseTestCases:
class BaseTest(unittest.TestCase):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTestCases.BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTestCases.BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __== '__main__':
unittest.main()
Die Ausgabe:
Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Sie können dieses Problem mit einem einzigen Befehl lösen:
del(BaseTest)
Der Code würde also so aussehen:
import unittest
class BaseTest(unittest.TestCase):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
del(BaseTest)
if __== '__main__':
unittest.main()
Matthew Marshalls Antwort ist großartig, aber es erfordert, dass Sie in jedem Ihrer Testfälle zwei Klassen erben, was fehleranfällig ist. Stattdessen verwende ich das (Python> = 2.7):
class BaseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
if cls is BaseTest:
raise unittest.SkipTest("Skip BaseTest tests, it's a base class")
super(BaseTest, cls).setUpClass()
Was willst du erreichen? Wenn Sie über allgemeinen Testcode (Assertions, Vorlagentests usw.) verfügen, platzieren Sie sie in Methoden, denen nicht test
vorangestellt ist, sodass unittest
sie nicht lädt.
import unittest
class CommonTests(unittest.TestCase):
def common_assertion(self, foo, bar, baz):
# whatever common code
self.assertEqual(foo(bar), baz)
class BaseTest(CommonTests):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(CommonTests):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(CommonTests):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __== '__main__':
unittest.main()
Matthews Antwort ist die, die ich verwenden musste, seit ich auf 2.5 bin. Aber ab 2.7 können Sie den Dekorator @ unittest.skip () für alle Testmethoden verwenden, die Sie überspringen möchten.
http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures
Sie müssen einen eigenen Ausblenddekorator implementieren, um den Basistyp zu überprüfen. Ich habe diese Funktion bisher noch nicht verwendet, aber ganz oben in meinem Kopf könnten Sie BaseTest als marker -Typ verwenden, um das Überspringen zu unterdrücken:
def skipBaseTest(obj):
if type(obj) is BaseTest:
return unittest.skip("BaseTest tests skipped")
return lambda func: func
Eine andere Option ist nicht auszuführen
unittest.main()
Anstelle dessen können Sie verwenden
suite = unittest.TestLoader().loadTestsFromTestCase(TestClass)
unittest.TextTestRunner(verbosity=2).run(suite)
Sie führen also nur die Tests in der Klasse TestClass
aus.
Ich habe mir überlegt, wie ich dieses Problem lösen könnte, indem man die Testmethoden verbirgt, falls die Basisklasse verwendet wird. Auf diese Weise werden die Tests nicht übersprungen, sodass die Testergebnisse in vielen Testberichts-Tools grün anstatt gelb sein können.
Im Vergleich zur Mixin-Methode beklagen sich Ide's wie PyCharm nicht darüber, dass Komponententestmethoden in der Basisklasse fehlen.
Wenn eine Basisklasse von dieser Klasse erbt, müssen die Methoden setUpClass
und tearDownClass
überschrieben werden.
class BaseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._test_methods = []
if cls is BaseTest:
for name in dir(cls):
if name.startswith('test') and callable(getattr(cls, name)):
cls._test_methods.append((name, getattr(cls, name)))
setattr(cls, name, lambda self: None)
@classmethod
def tearDownClass(cls):
if cls is BaseTest:
for name, method in cls._test_methods:
setattr(cls, name, method)
cls._test_methods = []
Ich habe ungefähr das gleiche gemacht wie @Vladim P. ( https://stackoverflow.com/a/25695512/2451329 ), aber etwas modifiziert:
import unittest2
from some_module import func1, func2
def make_base_class(func):
class Base(unittest2.TestCase):
def test_common1(self):
print("in test_common1")
self.assertTrue(func())
def test_common2(self):
print("in test_common1")
self.assertFalse(func(42))
return Base
class A(make_base_class(func1)):
pass
class B(make_base_class(func2)):
def test_func2_with_no_arg_return_bar(self):
self.assertEqual("bar", func2())
und los geht's.
Das ist also ein alter Faden, aber ich bin heute auf dieses Problem gestoßen und habe mir meinen eigenen Hack dafür gedacht. Es verwendet einen Dekorator, der die Werte der Funktionen auf Keine setzt, wenn über die Basisklasse zugegriffen wird. Machen Sie sich keine Gedanken über Setup und Setupclass, denn wenn die Baseclass keine Tests hat, werden sie nicht ausgeführt.
import types
import unittest
class FunctionValueOverride(object):
def __init__(self, cls, default, override=None):
self.cls = cls
self.default = default
self.override = override
def __get__(self, obj, klass):
if klass == self.cls:
return self.override
else:
if obj:
return types.MethodType(self.default, obj)
else:
return self.default
def fixture(cls):
for t in vars(cls):
if not callable(getattr(cls, t)) or t[:4] != "test":
continue
setattr(cls, t, FunctionValueOverride(cls, getattr(cls, t)))
return cls
@fixture
class BaseTest(unittest.TestCase):
def testCommon(self):
print('Calling BaseTest:testCommon')
value = 5
self.assertEqual(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print('Calling SubTest1:testSub1')
sub = 3
self.assertEqual(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print('Calling SubTest2:testSub2')
sub = 4
self.assertEqual(sub, 4)
if __== '__main__':
unittest.main()
Benennen Sie die testCommon-Methode einfach in etwas anderes um. Unittest (normalerweise) überspringt alles, was keinen 'Test' enthält.
Schnell und einfach
import unittest
class BaseTest(unittest.TestCase):
def methodCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __== '__main__':
unittest.main()`
Sie können __test_ = False
in der BaseTest-Klasse hinzufügen. Beachten Sie jedoch, dass Sie __test__ = True
in abgeleiteten Klassen hinzufügen müssen, um Tests ausführen zu können.
import unittest
class BaseTest(unittest.TestCase):
__test__ = False
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
__test__ = True
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
__test__ = True
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __== '__main__':
unittest.main()
Ab Python 3.2 können Sie einem Modul eine test_loader - Funktion hinzufügen, um zu steuern, welche Tests (falls vorhanden) vom Testerkennungsmechanismus gefunden werden.
Im Folgenden werden beispielsweise nur die SubTest1
- und SubTest2
-Testfälle des ursprünglichen Posters geladen, wobei Base
ignoriert wird:
def load_tests(loader, standard_tests, pattern):
suite = TestSuite()
suite.addTests([SubTest1, SubTest2])
return suite
Es sollte möglich sein, über standard_tests
(eine TestSuite
, die die Tests enthält, die der Standard-Loader gefunden hat) zu iterieren und stattdessen alle Variablen außer Base
in suite
zu kopieren, aber die verschachtelte Natur von TestSuite.__iter__
macht dies viel komplizierter.