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

Python unittest с дорогой настройкой

Мой тестовый файл в основном:

class Test(unittest.TestCase):
    def testOk():
        pass

if __name__ == "__main__":
    expensiveSetup()
    try:
        unittest.main()
    finally:
        cleanUp()

Тем не менее, я хочу запустить тест через инструменты тестирования Netbeans, и для этого мне нужны unittests, которые не полагаются на настройку среды, выполненную в main. Глядя на Кэширующий результат setUp() с помощью Python unittest - он рекомендует использовать Nose. Однако я не думаю, что Netbeans это поддерживает. Я не нашел никакой информации о том, что она делает. Кроме того, я единственный, кто на самом деле пишет тесты, поэтому я не хочу вводить дополнительные зависимости для других 2 разработчиков, если они не нужны.

Как я могу выполнить настройку и очистку один раз для всех тестов в своем TestSuite?

Дорогая настройка здесь - это создание некоторых файлов с фиктивными данными, а также настройка и отключение простого сервера xml-rpc. У меня также есть 2 тестовых класса, одно тестирование локально и одно тестирование всех методов по xml-rpc.

4b9b3361

Ответ 1

Если вы используете Python >= 2.7 (или unittest2 для Python >= 2.4 и <= 2.6), наилучший подход быть использовать

def setUpClass(cls):
    # ...
setUpClass = classmethod(setUpClass)

выполнить некоторую инициализацию один раз для всех тестов, принадлежащих данному классу.

И для выполнения очистки используйте:

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

См. также стандартную библиотеку unittest документацию по методам setUpClass и tearDownClass.

Ответ 2

Это то, что я делаю:

class TestSearch(unittest.TestCase):
    """General Search tests for...."""

    matcher = None
    counter      = 0
    num_of_tests = None

    def setUp(self): # pylint: disable-msg=C0103 
        """Only instantiate the matcher once"""
        if self.matcher is None:
            self.__class__.matcher = Matcher()
            self.__class__.num_of_tests = len(filter(self.isTestMethod, dir(self)))
        self.__class__.counter = self.counter + 1 

    def tearDown(self): # pylint: disable-msg=C0103
        """And kill it when done"""
        if self.counter == self.num_of_tests:
            print 'KILL KILL KILL'
            del self.__class__.matcher

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

Ответ 3

Прежде всего, что сказал С. Лотт. Однако, вы не хотите этого делать. Существует причина, по которой setUp и tearDown обернуты вокруг каждого теста: они помогают сохранить детерминизм тестирования.

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

Кроме того, если вы настаиваете на том, чтобы делать это таким образом, вместо того чтобы писать вручную self.runTest1(), self.runTest2(), вам может понадобиться немного интроспекции, чтобы найти методы для запуска.

Ответ 4

Не будет ли инициализация на уровне пакетов сделать это за вас? Из Nose Wiki:

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

Чтобы создать настройку на уровне пакета и методы удаления, определить настройку и/или функции отрыва в __init__.pyтестового пакета. Способы настройки могут называть setup, setup_package, setup, или setUpPackage; разрывание можно назвать teardown, teardown_package, teardownили tearDownPackage. Выполнение тестов в тестовом пакете начинается, как только первый тестовый модуль загружается из тестовый пакет.

Ответ 5

Вы можете сохранить состояние, если expensiveSetup() запущено или нет.

__expensiveSetup_has_run = False

class ExpensiveSetupMixin(unittest.TestCase):
    def setUp(self):
        global __expensiveSetup_has_run
        super(ExpensiveSetupMixin, self).setUp()
        if __expensiveSetup_has_run is False:
            expensiveSetup()
            __expensiveSetup_has_run = True

Или какая-то вариация этого. Возможно, pinging сервер xml-rpc и создать новый, если он не отвечает.

Но метод unit-testing AFAIK - настройка и отключение в unittest, даже если это дорого.

Ответ 6

Я ничего не знаю о Netbeans, но я должен упомянуть zope.testrunner, и он поддерживает отличную вещь: Слои. В принципе, вы выполняете набор тестов в отдельных классах и присоединяете эти классы к тестам. Эти классы могут наследовать друг от друга, формируя слой настроек. Затем тесторунтер будет только вызывать каждую настройку один раз и сохранять состояние этого в памяти, а вместо того, чтобы настраивать и срывать, просто просто скопирует соответствующий контекст слоя в качестве настройки.

Это значительно ускоряет тестовую настройку и используется, когда вы тестируете продукты Zope и Plone, где для тестирования часто требуется, чтобы вы запустили сервер Plone CMS, создали сайт Plone и добавили множество контента, процесс, который может потребоваться на полминуты. Выполнение этого для каждого метода тестирования, очевидно, невозможно, но со слоями это делается только один раз. Это сокращает тестовую настройку и защищает методы тестирования друг от друга, и поэтому означает, что тестирование продолжает оставаться неопределенным.

Поэтому я не знаю, как zope.testrunner будет работать на вас, но стоит попробовать.

Ответ 7

Вы можете заверить setUp и tearDown выполнить один раз, если у вас есть только один метод тестирования, runTest. Этот метод может делать все, что он хочет. Просто убедитесь, что у вас нет методов с именами, начинающимися с test.

class MyExpensiveTest( unittest.TestCase ):
    def setUp( self ):
        self.resource = owThatHurts()
    def tearDown( self ):
        self.resource.flush()
        self.resource.finish()
    def runTest( self ):
        self.runTest1()
        self.tunTest2()
    def runTest1( self ):
        self.assertEquals(...)
    def runTest2( self ):
        self.assertEquals(...)

Он не умеет автоматически определять, что нужно запускать. Если вы добавите метод тестирования, вам также необходимо обновить runTest.