Как вы можете очистить Python UnitTest, когда setUpClass не работает?

Скажем, у меня есть следующий Python UnitTest:

import unittest

def Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Get some resources
        ...

        if error_occurred:
            assert(False)

    @classmethod
    def tearDownClass(cls):
        # release resources
        ...

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

Есть ли способ сделать очистку, когда вызов setUpClass не работает?


person sickgemini    schedule 09.07.2013    source источник
comment
Что-то плохо пахнет в этом сценарии. Что вы пытаетесь протестировать?   -  person fabrizioM    schedule 09.07.2013
comment
@fabrizioM: Что тебе в этом не нравится? Я запускаю множество тестовых случаев, которые используют некоторые общие ресурсы. Чтобы сократить время выполнения теста, я не хочу каждый раз настраивать общие ресурсы.   -  person sickgemini    schedule 09.07.2013
comment
Спасибо @fabrizioM. Согласен насчет интеграции и модульных тестов, но, к сожалению, приходится работать с тем, что уже реализовано.   -  person sickgemini    schedule 09.07.2013
comment
addCleanup() работает для потестовой очистки, но я, увы, не нашел ничего эквивалентного для очистки на уровне класса. Кажется, что менее элегантная попытка / кроме приведенных ниже рекомендаций — это лучшее, что мы можем сделать.   -  person NeilenMarais    schedule 18.04.2016


Ответы (3)


вы можете поместить try catch в метод setUpClass и напрямую вызвать tearDown в исключении.

def setUpClass(cls):
     try: 
         # setUpClassInner()
     except Exception, e:
        cls.tearDownClass()
        raise # to still mark the test as failed.

Требование внешних ресурсов для запуска вашего юнит-теста — плохая практика. Если эти ресурсы недоступны и вам нужно проверить часть кода на наличие странной ошибки, вы не сможете быстро запустить его. Попробуйте отличить интеграционные тесты от модульных тестов.

person fabrizioM    schedule 09.07.2013
comment
Предположение, что каждый, кто использует пакет unittest, реализует настоящие модульные тесты, в наши дни не совсем верно. (Или, если вы меня, в этом столетии..) Теперь должно быть общеизвестно, что этот пакет является королем неправильных названий в ядре python, потому что он как минимум способствует реализации интеграции высокого уровня или системные тесты, поскольку это настоящие модульные тесты. - person timblaktu; 27.07.2018

Так же, как вы защищаете ресурсы в другом месте. try-except:

def setUpClass(cls):
    # ... acquire resources 
    try:
        # ... some call that may fail
    except SomeError, e:
        # cleanup here

Очистка может быть такой же простой, как вызов cls.tearDownClass() в вашем блоке except. Затем вы можете вызвать assert(False) или любой другой метод, который вы предпочитаете, чтобы выйти из теста досрочно.

person ACEfanatic02    schedule 09.07.2013

У меня есть целая куча тестовых вспомогательных функций, которые берут тестовый экземпляр и используют addCleanup для чистой настройки/удаления потоков, временных файлов и т. д., поэтому мне нужно, чтобы addCleanup API работал и с приборами уровня класса. Я повторно реализовал часть функциональности doCleanup из unittest, чтобы помочь мне, и использовал mock для исправления addCleanup() во время настройки класса.

import unittest
import logging
import mock

LOGGER = logging.getLogger(__name__)

class ClassCleanupTestCase(unittest.TestCase):
    _class_cleanups = []

    @classmethod
    def setUpClassWithCleanup(cls):
        def cleanup_fn():
            """Do some cleanup!"""
        # Do something that requires cleanup
        cls.addCleanup(cleanup_fn)

    @classmethod
    def addCleanupClass(cls, function, *args, **kwargs):
        cls._class_cleanups.append((function, args, kwargs))

    @classmethod
    def doCleanupsClass(cls):
        results = []
        while cls._class_cleanups:
            function, args, kwargs = cls._class_cleanups.pop()
            try:
                function(*args, **kwargs)
            except Exceptions:
                LOGGER.exception('Exception calling class cleanup function')
                results.append(sys.exc_info())

        if results:
            LOGGER.error('Exception(s) raised during class cleanup, re-raising '
                         'first exception.')
            raise results[0]

    @classmethod
    def setUpClass(cls):
        try:
            with mock.patch.object(cls, 'addCleanup') as cls_addCleanup:
                cls_addCleanup.side_effect = cls.addCleanupClass
                cls.setUpClassWithCleanup()
        except Exception:
            cls.doCleanupsClass()
            raise

    @classmethod
    def tearDownClass(cls):
        cls.doCleanupsClass()
person NeilenMarais    schedule 18.04.2016