Python unit test с базовым и подклассом
В настоящее время у меня есть несколько модульных тестов, которые содержат общий набор тестов. Вот пример:
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 __name__ == '__main__':
unittest.main()
Вывод выше:
Calling BaseTest:testCommon
.Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
Есть ли способ переписать выше, чтобы первый testCommon
не вызывался?
EDIT:
Вместо того, чтобы запускать 5 тестов выше, я хочу, чтобы он запускал только 4 теста, 2 из SubTest1 и еще 2 из SubTest2. Кажется, что Python unittest запускает оригинальный BaseTest сам по себе, и мне нужен механизм, чтобы это не происходило.
Ответы
Ответ 1
Используйте множественное наследование, поэтому ваш класс с общими тестами сам не наследует TestCase.
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 __name__ == '__main__':
unittest.main()
Ответ 2
Не используйте множественное наследование, оно укусит вас позже.
Вместо этого вы можете просто переместить базовый класс в отдельный модуль или обернуть его пустым классом:
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 __name__ == '__main__':
unittest.main()
Выход:
Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Ответ 3
Вы можете решить эту проблему с помощью одной команды:
del(BaseTest)
Таким образом, код будет выглядеть так:
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 __name__ == '__main__':
unittest.main()
Ответ 4
Ответ Мэтью Маршалла велик, но для этого требуется, чтобы вы наследовали два класса в каждом из ваших тестовых случаев, что подвержено ошибкам. Вместо этого я использую это (python >= 2.7):
class BaseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
if cls is BaseTest:
raise unittest.SkipTest("Skip BaseTest tests, it a base class")
super(BaseTest, cls).setUpClass()
Ответ 5
Чего вы пытаетесь достичь? Если у вас есть общий тестовый код (утверждения, тесты шаблонов и т.д.), Поместите их в методы, которые не префиксны с помощью test
, поэтому unittest
не загрузит их.
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 __name__ == '__main__':
unittest.main()
Ответ 6
Ответ Мэтью - тот, который мне нужно использовать, так как я все еще нахожусь на 2.5.
Но с 2,7 вы можете использовать декоратор @unittest.skip() для любых методов тестирования, которые вы хотите пропустить.
http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures
Вам нужно будет реализовать свой собственный пропущенный декоратор, чтобы проверить базовый тип. Раньше не использовали эту функцию, но с верхней части головы вы могли использовать BaseTest в качестве типа маркер для условия пропуска:
def skipBaseTest(obj):
if type(obj) is BaseTest:
return unittest.skip("BaseTest tests skipped")
return lambda func: func
Ответ 7
Другой вариант - не выполнять
unittest.main()
Вместо этого вы можете использовать
suite = unittest.TestLoader().loadTestsFromTestCase(TestClass)
unittest.TextTestRunner(verbosity=2).run(suite)
Итак, вы выполняете только тесты в классе TestClass
Ответ 8
Способ, которым я решил решить это, скрывает методы тестирования, если используется базовый класс. Таким образом, тесты не пропускаются, поэтому результаты тестирования могут быть зелеными, а не желтыми во многих инструментах отчетов о тестировании.
По сравнению с методом mixin, такой как PyCharm, не будет жаловаться на отсутствие методов unit test в базовом классе.
Если базовый класс наследуется от этого класса, он должен будет переопределить методы setUpClass
и tearDownClass
.
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 = []
Ответ 9
Просто переименуйте метод testCommon на что-то еще. Unittest (обычно) пропускает все, что не имеет в нем "теста".
Быстрый и простой
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 __name__ == '__main__':
unittest.main()`
Ответ 10
Я сделал примерно то же, что @Vladim P. (fooobar.com/questions/59545/...), но слегка изменил:
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())
и там мы идем.
Ответ 11
Измените имя метода BaseTest на setUp:
class BaseTest(unittest.TestCase):
def setUp(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)
Выход:
Ran 2 тесты в 0,000 с
Вызов BaseTest: testCommon Calling
SubTest1: testSub1 Вызов
BaseTest: testCommon Calling
SubTest2: testSub2
Из документация:
TestCase.setUp()
Метод, призванный подготовить тестовое крепление. Это вызывается непосредственно перед вызовом Метод испытания; любое исключение, вызванное этот метод будет рассматриваться как а не с ошибкой теста. реализация по умолчанию ничего не делает.