Как добавить тестовый метод в группу классов, производных от Django TestCase? - PullRequest
11 голосов
/ 31 августа 2010

У меня есть группа тестовых случаев, для которых все должны выполнить один и тот же тест, в соответствии со словами «Возвращает ли метод x имя существующего файла?»

Я подумал, что лучшим способом сделать это будет базовый класс, производный от TestCase, который они все разделяют, и просто добавить тест в этот класс. К сожалению, среда тестирования все еще пытается запустить тест для базового класса, где это не имеет смысла.

class SharedTest(TestCase):
    def x(self):
        ...do test...

class OneTestCase(SharedTest):
    ...my tests are performed, and 'SharedTest.x()'...

Я попытался взломать чек, чтобы просто пропустить тест, если он вызывается для объекта базового класса, а не для производного класса, например:

    class SharedTest(TestCase):
        def x(self):
            if type(self) != type(SharedTest()):
                ...do test...
            else:
                pass

но получил эту ошибку:

ValueError: no such test method in <class 'tests.SharedTest'>: runTest

Во-первых, я хотел бы получить любые элегантные предложения для этого. Во-вторых, хотя я не хочу использовать хак типа (), я хотел бы понять, почему он не работает.

Ответы [ 2 ]

25 голосов
/ 31 августа 2010

Вы можете использовать миксин, воспользовавшись тем, что бегущий тест запускает только тесты, унаследованные от unittest.TestCase (от которого у Джанго TestCase наследуется.) Например:

class SharedTestMixin(object):
    # This class will not be executed by the test runner (it inherits from object, not unittest.TestCase.
    # If it did, assertEquals would fail , as it is not a method that exists in `object`
    def test_common(self):
         self.assertEquals(1, 1)


class TestOne(TestCase, SharedTestMixin):
    def test_something(self):
         pass

    # test_common is also run

class TestTwo(TestCase, SharedTestMixin):
    def test_another_thing(self):
        pass

    # test_common is also run

Для получения дополнительной информации о причинахэто работает для поиска порядка разрешения методов python и множественного наследования.

2 голосов
/ 31 августа 2010

Я столкнулся с подобной проблемой. Я не мог помешать выполнению тестового метода в базовом классе, но я гарантировал, что он не выполняет никакого реального кода. Я сделал это путем проверки атрибута и немедленного возврата, если он был установлен. Этот атрибут был установлен только для базового класса, и поэтому тесты выполнялись везде, кроме базового класса.

class SharedTest(TestCase):
    def setUp(self):
        self.do_not_run = True

    def test_foo(self):
        if getattr(self, 'do_not_run', False):
            return
        # Rest of the test body.

class OneTestCase(SharedTest):
    def setUp(self):
        super(OneTestCase, self).setUp()
        self.do_not_run = False

Это что-то вроде хака. Существует вероятно лучший способ сделать это , но я не уверен, как .

Обновление

Как сказал Сдолан , миксин - правильный путь. Почему я не видел этого раньше?

Обновление 2

(После прочтения комментариев) Было бы неплохо, если бы (1) метод суперкласса мог избежать хакерской проверки if getattr(self, 'do_not_run', False):; (2) если количество тестов было подсчитано точно.

Существует возможный способ сделать это. Django подхватывает и выполняет все тестовые классы в tests, будь то tests.py или пакет с таким именем. Если тестовый суперкласс объявлен вне тестового модуля, то этого не произойдет. Это все еще может быть унаследовано тестовыми классами. Например, SharedTest может быть расположен в app.utils и затем использоваться тестовыми примерами. Это будет более чистая версия вышеуказанного решения.

# module app.utils.test
class SharedTest(TestCase):
    def test_foo(self):
        # Rest of the test body.

# module app.tests
from app.utils import test
class OneTestCase(test.SharedTest):
    ...
...