Можно ли создать абстрактный TestCase
, который будет иметь некоторые методы test_ *, но этот TestCase
не будет вызываться, и эти методы будут использоваться только в подклассах? Я думаю, что у меня будет один абстрактный TestCase
в моем тестовом наборе, и он будет подклассифицирован для нескольких разных реализаций одного интерфейса. Вот почему все методы тестирования - это некоторые, только один, внутренний метод изменений. Как я могу сделать это элегантным способом?
Абстрактный тестовый пример с использованием python unittest
Ответ 1
Я не совсем понял, что вы планируете делать - эмпирическое правило "не должно быть умным с тестами" - просто имейте их там, написанный просто.
Но для достижения того, чего вы хотите, если вы наследуете unittest.TestCase, всякий раз, когда вы вызываете unittest.main(), ваш "абстрактный" класс будет выполнен - я думаю, что это та ситуация, которую вы хотите избежать.
Просто выполните следующее: Создайте свой "абстрактный" класс, наследующий от "объекта", а не от TestCase. И для реальных "конкретных" реализаций просто используйте множественное наследование: наследуйте как от unittest.TestCase, так и от вашего абстрактного класса.
import unittest
class Abstract(object):
def test_a(self):
print "Running for class", self.__class__
class Test(Abstract, unittest.TestCase):
pass
unittest.main()
update: сначала измените порядок наследования - Abstract
, чтобы его исправления не были переопределены параметрами TestCase
по умолчанию, также указанными в комментариях ниже.
Ответ 2
Множественное наследование здесь не является отличным вариантом, главным образом по двум следующим причинам:
- Ни один из методов в
TestCase
не используетsuper()
, поэтому вам нужно будет сначала указать свой класс для таких методов, какsetUp()
иtearDown()
. - pylint предупредит, что базовый класс использует
self.assertEquals()
и т.д., которые не определены вself
в этой точке.
Здесь появился kludge: turn run()
в no-op только для базового класса.
class TestBase( unittest.TestCase ):
def __init__( self, *args, **kwargs ):
super( TestBase, self ).__init__( *args, **kwargs )
self.helper = None
# Kludge alert: We want this class to carry test cases without being run
# by the unit test framework, so the `run' method is overridden to do
# nothing. But in order for sub-classes to be able to do something when
# run is invoked, the constructor will rebind `run' from TestCase.
if self.__class__ != TestBase:
# Rebind `run' from the parent class.
self.run = unittest.TestCase.run.__get__( self, self.__class__ )
else:
self.run = lambda self, *args, **kwargs: None
def newHelper( self ):
raise NotImplementedError()
def setUp( self ):
print "shared for all subclasses"
self.helper = self.newHelper()
def testFoo( self ):
print "shared for all subclasses"
# test something with self.helper
class Test1( TestBase ):
def newHelper( self ):
return HelperObject1()
class Test2( TestBase ):
def newHelper( self ):
return HelperObject2()
Ответ 3
Как раз для ввода двухцентов, хотя это, вероятно, противоречит некоторым соглашениям, вы можете определить свой абстрактный тестовый пример как защищенный член, чтобы предотвратить его выполнение. Я реализовал следующее в Django и работает по мере необходимости. См. Пример ниже.
from django.test import TestCase
class _AbstractTestCase(TestCase):
"""
Abstract test case - should not be instantiated by the test runner.
"""
def test_1(self):
raise NotImplementedError()
def test_2(self):
raise NotImplementedError()
class TestCase1(_AbstractTestCase):
"""
This test case will pass and fail.
"""
def test_1(self):
self.assertEqual(1 + 1, 2)
class TestCase2(_AbstractTestCase):
"""
This test case will pass successfully.
"""
def test_1(self):
self.assertEqual(2 + 2, 4)
def test_2(self):
self.assertEqual(12 * 12, 144)
Ответ 4
Если вы следуете за соглашением о явном перечислении всех тестовых классов в run_unittest (см., например, набор тестов Python для многих применений этого соглашения), тогда будет просто не указывать конкретный класс.
Если вы хотите продолжить использование unittest.main, и если вы можете разрешить использование unittest2 (например, из Python 2.7), вы можете использовать load_tests, чтобы указать, какие классы содержат тестовые примеры). В более ранних версиях вам придется подклассифицировать TestLoader и переопределить loadTestsFromModule.
Ответ 5
В библиотеке python unittest есть протокол load_tests, который можно использовать для достижения именно того, что вы хотите:
# Add this function to module with AbstractTestCase class
def load_tests(loader, tests, _):
result = []
for test_case in tests:
if type(test_case._tests[0]) is AbstractTestCase:
continue
result.append(test_case)
return loader.suiteClass(result)
Ответ 6
Там очень простой способ, который все пропустили до сих пор. И в отличие от нескольких ответов, он работает со всеми тестовыми драйверами, а не с той минуты, когда вы переключаетесь между ними.
Просто используйте наследование как обычно, затем добавьте:
del AbstractTestCase
в конце модуля.