Подтвердить что ты не робот

Абстрактный тестовый пример с использованием python unittest

Можно ли создать абстрактный TestCase, который будет иметь некоторые методы test_ *, но этот TestCase не будет вызываться, и эти методы будут использоваться только в подклассах? Я думаю, что у меня будет один абстрактный TestCase в моем тестовом наборе, и он будет подклассифицирован для нескольких разных реализаций одного интерфейса. Вот почему все методы тестирования - это некоторые, только один, внутренний метод изменений. Как я могу сделать это элегантным способом?

4b9b3361

Ответ 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

в конце модуля.