Python/Справочник по библиотеке Python 2.6: различия между версиями

Содержимое удалено Содержимое добавлено
Строка 118:
<p>Для достижения таких целей в '''unittest''' реализуются следующие понятия:</p>
 
 
<ul><li>
;Окружение теста (test fixture)<br>:
Под окружением теста подразумевается установка начальных данных, необходимая для выполнения одного или нескольких тестов, а также действия по сбросу начальных данных к исходному положению по завершении теста. Эти процессы могут включать, например, создание временных или промежуточных БД, каталогов, или запуск серверного процесса;</li><li>
;Тестовый вариант (test case).<br>:
Тестовый вариант является наименьшим элементом тестирования. Он проверяет соответствие отклика объекта определенным входным данным. В unittest тестовые варианты создаются с помощью базового класса '''TestCase''';
</li><li>;Набор тестов (test suite)<br>:
Набор тестов это коллекция тестовых вариантов, наборов тестов, или и того и другого сразу. Набор используется для объединения тестов которые должны выполняться вместе;
</li><li>;Исполнитель тестов<br>:
Исполнитель тестов это компонент управляющий выполнением тестов и сообщающий пользователю результаты выполнения. Исполнитель может использовать графический, или текстовый интерфейс, либо возвращать определенное значение, соответствующее результату выполнения тестов.
 
</li></ul>
 
Понятия тестового варианта и окружения теста реализуются классами '''TestCase''' и '''FunctionTestCase'''; первый следует использовать при создании новых тестов, а второй используется при интеграции кода уже написанных тестов с каркасом модульных тестов. При создании тестового окружения с помощью '''TestCase''', методы '''setUp()''' и '''tearDown()''' могут замещаться для обеспечения инициализации и сброса окружения. При использовании '''FunctionTestCase''', для этих целей в конструктор могут передаваться уже существующие функции. При выполнении теста сначала запускается инициализация окружения, если она проходит успешно, то после завершения теста запускается функция сброса окружения, вне зависимости от результатов теста. Для каждого экземпляра '''TestCase''' выполняется только единственный тестовый метод, поэтому для каждого теста создается новое окружение.
Строка 148:
 
====25.3.1 Простой пример====
Модуль '''unittest''' содержит богатый набор инструментовсредств для создания и выполнения тестов. В данном разделе демонстрируется, что даже малая часть этих средств достаточна для удовлетворения нужд большинства пользователей.
 
Вот короткий сценарий для тестирования трех функций из модуля random:
Вот короткий сценарий для тестирования трех функций из модуля '''random''':
import random
import unittest
 
<source lang="python">
import random
import unittest
class TestSequenceFunctions(unittest.TestCase):
 
Строка 179 ⟶ 180 :
</source>
 
Тестовый вариант создан как подкласс '''unittest.TestCase'''. Три отдельных теста заданы с помощью методов, названия которых начинаются с последовательности 'test'. Такое соглашение об наименовании сообщает исполнитель тестов о том, какие методы представляют тесты.
 
Главной частью всех тестов является вызов '''assertEqual()''' для проверки ожидаемого результата, '''assertTrue()''' для проверки условия, либо '''assertRaises()''' для проверки возбуждения требуемого исключения. Эти методы используются вместо выражения '''assert''', для того чтобы исполнитель тестов мог собрать результаты всех тестов и сформировать отчет.
Если задан метод setUp(), исполнитель тестов выполнит этот метод перед выполнением каждого теста. Аналогично, если задан метод tearDown(), этот метод будет выполняться после каждого теста. В данном примере setUp() использовался для создания свежей последовательности для каждого теста.
В последнем блоке демонстрируется простой способ выполнения тестов. unittest.main() предоставляет командно-строчный интерфейс к тестовому сценарию. При запуске из командной строки, представленный выше сценарий формирует вывод следующего вида:
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
 
Если задан метод '''setUp()''', исполнитель тестов вызовет этот метод перед выполнением каждого теста. Аналогично, если задан метод '''tearDown()''', этот метод будет выполняться после каждого теста. В рассмотренном примере '''setUp()''' использовался для создания свежей последовательности для каждого теста.
OK
 
В последнем блоке демонстрируется простой способ выполнения тестов. '''unittest.main()''' предоставляет командно-строчный интерфейс к тестовому сценарию. При запуске из командной строки, представленный выше сценарий формирует вывод следующего вида:
Существуют и другие способы выполнение тестов, кроме unittest.main(), причем с большей степенью контроля, не таким скупым выводом и не обязательно запускаемые из командной строки. Например, последние две строки могут быть заменены следующими:
 
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
 
Существуют и другие способы выполнение тестов, кроме '''unittest.main()''', причем с большей степенью контроля, не таким скупым выводом и не обязательно запускаемые из командной строки. Например, последние две строки могут быть заменены следующими:
<source lang="python">
suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)
</source>
 
Результатом выполнения измененного сценария из интерпретатора, либо из другого сценария , будет следующий вывод:
test_choice (__main__.TestSequenceFunctions) ... ok
test_sample (__main__.TestSequenceFunctions) ... ok
test_shuffle (__main__.TestSequenceFunctions) ... ok
 
test_choice (__main__.TestSequenceFunctions) ... ok
----------------------------------------------------------------------
test_sample (__main__.TestSequenceFunctions) ... ok
Ran 3 tests in 0.110s
test_shuffle (__main__.TestSequenceFunctions) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.110s
OK
 
Приведенные выше примеры демонстрируют наиболее часто используемые функции '''unittest''', достаточные для многих каждодневных задач тестирования. Далее рассматривается полный набор функций, начиная с основных принципов
OK
 
====Организация тестового кода====
Приведенные выше примеры демонстрируют наиболее часто используемые функции unittest, достаточные для многих каждодневных задач тестирования. Далее рассматривается полный набор функций, начиная с основных принципов
 
Основными строительными блоками модульного тестирования являются тестовые варианты – отдельные сценарии, для которые должны устанавливаться начальные значения и проводиться проверка на правильность. В модуле '''unittest''', тестовые варианты представлены экземплярами класса '''TestCase'''. Для написания своего тестового сценария Вам требуется написать подкласс класса '''TestCase''', либо использовать '''FunctionTestCase'''.
====25.3.2 Организация тестового кода====
 
Основными строительными блоками модульного тестирования являются тестовые варианты – отдельные сценарии, для которые должны устанавливаться начальные значения и проводиться проверка на правильность. В модуле unittest, тестовые варианты представлены экземплярами класса TestCase. Для написания своего тестового сценария Вам требуется написать подкласс класса TestCase, либо использовать FunctionTestCase.
Экземпляр класса производного от '''TestCase''' является объектом который полностью выполняетвыполняющим единственный тестовый метод вместе с необязательным кодом настройки и сброса.
 
Тестовый код экземпляра TestCase должен быть полностью самодостаточным, так, чтобы он мог выполняться изолированно, либо в произвольной комбинации с любым количеством других тестовых вариантов.
Тестовый код экземпляра '''TestCase''' должен быть полностью самодостаточным, так, чтобы он мог выполняться изолированно, либо в произвольной комбинации с любым количеством других тестовых вариантов.
Простейший подкласс TestCase просто замещает метод runTest() для выполнения определенного тестового кода:
Простейший подкласс '''TestCase''' просто замещает метод '''runTest()''' для выполнения определенного тестового кода:
 
<source lang="python">
import unittest
 
class DefaultWidgetSizeTestCase(unittest.TestCase):
def runTest(self):
widget = Widget('The widget')
self.assertEqual(widget.size(), (50, 50), 'incorrect default size')
</source>
 
Обратите внимание, что для того чтобы протестировать что-либо, используется один из методов '''assert*()''' или '''fail*()''' базового класса '''TestCase()'''. Если тест проваливается, поднимается исключение, и '''unittest''' идентифицирует тестовый вариант как «провалившийся» (failure). Любые иные исключения воспринимаются как ошибки (errors). Такое разделение позволяет определить место проблемы: ''failure'' вызываются неправильными результатами – 5 в том месте, в котором ожидается 6. ''Errors'' вызываются некорректным кодом – например, причиной'''TypeError''' является неправильный вызов функции
 
Обратите внимание, что для того чтобы протестировать что-либо, используется один из методов assert*() или fail*() базового класса TestCase(). Если тест проваливается, поднимается исключение, и unittest идентифицирует тестовый вариант как «провалившийся» (failure). Любые иные исключения воспринимаются как ошибки (errors). Такое разделение позволяет определить место проблемы: failure вызываются неправильными результатами – 5 в том месте, в котором ожидается 6. Errors вызываются некорректным кодом ¬¬¬– например, причиной TypeError является неправильный вызов функции
Способ выполнения тестовых вариантов будет описан чуть позже. Сейчас же, отметьте что для того чтобы создать экземпляр такого тестового варианта вызывается его конструктор без аргументов:
 
<source lang="python">
testCase = DefaultWidgetSizeTestCase()
</source>
Подобные тестовые варианты могут быть многочисленными и установка начальных значений для них может повторяться многократно. В примере выше, создание виджета для каждого из 100 тестовых вариантов для виджетов означает некрасивое дуплицирование кода.
К счастью, можно избежать таких проблем с кодом установки начальных значений, используя метод называемый setUp(), автоматически вызываемый тестовым каркасом при выполнении теста:
 
Подобные тестовые варианты могут быть многочисленными и установка начальных значений для них может повторяться многократно. В примере выше, создание виджета для каждого из 100 тестовых вариантов для виджетов, означает некрасивое повторение кода.
 
К счастью, можно избежать таких проблем с кодом установки начальных значений, используя метод '''setUp()''', автоматически вызываемый тестовым каркасом при выполнении теста:
 
<source lang="python">
import unittest
 
Строка 240 ⟶ 260 :
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
</source>
Если метод setUp() поднимает исключение во время выполнения теста, каркас полагает что в тесте есть ошибка и метод runTest() не выполняется.
Аналогичными образом можно добавить метод tearDown() который сбрасывает изменения после выполнения метода runTest()
 
Если метод '''setUp()''' поднимает исключение во время выполнения теста, каркас полагает что в тесте есть ошибка и метод '''runTest()''' не выполняется.
Аналогичными образом можно добавить метод '''tearDown()''' который сбрасывает изменения в начальных установках после выполнения метода '''runTest()'''
 
<source lang="python">
class SimpleWidgetTestCase(unittest.TestCase):
def setUp(self):
Строка 250 ⟶ 273 :
self.widget.dispose()
self.widget = None
</source>
 
Если метод '''setUp()''' был успешно выполнен, метод '''tearDown()''' будет выполнятся вне зависимости от успешности выполнения '''runtTest()'''.
 
Если метод setUp() был успешно выполнен, метод tearDown будет выполнятся вне зависимости от успешности выполнения runtTest().
Описанная рабочая среда для тестового кода называется окружением (fixture).
Часто множество небольших тестовых вариантов используют одинаковое окружение. В таких случаях, для класса '''SimpleWidgetTestCase''' может создаваться куча подклассов с одним методом, типа '''DefaultWidgetSizeTestCase'''. Эта операция отнимает много времени и отбивает всякое желание ей заниматься, поэтому, аналогично с '''JUnit''', '''unittest''' предоставляет более простой механизм:
 
<source lang="python">
import unittest
 
Строка 272 ⟶ 299 :
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
</source>
 
В приведенном примере нет метода '''runTest()''', вместо этого есть два разных тестовых метода. Каждый экземпляр класса будут выполнять один из методов '''test_*()''' с созданием и уничтожением '''self.widget''' при запуске тестового метода. При создании экземпляра требуется указать какой тестовый метод следует выполнять. Это достигается передачей названия метода в конструктор.
 
<source lang="python">
defaultSizeTestCase = WidgetTestCase('test_default_size')
resizeTestCase = WidgetTestCase('test_resize')
</source>
Экземпляры тестовых вариантов группируются в соответствии с функциями, которые они тестируют. unittest предоставляет механизм такой группировки: тестовые наборы, представляемые классом TestSuite модуля unittest:
 
Экземпляры тестовых вариантов группируются в соответствии с функциями, которые они тестируют. '''unittest''' предоставляет механизм такой группировки: тестовые наборы, представляемые классом '''TestSuite''' модуля '''unittest''':
 
<source lang="python">
widgetTestSuite = unittest.TestSuite()
widgetTestSuite.addTest(WidgetTestCase('test_default_size'))
widgetTestSuite.addTest(WidgetTestCase('test_resize'))
</source>
 
Для упрощения выполнения тестов, как мы убедимся чуть позже, рекомендуется создать в каждом тестовом модуле вызываемый объект, возвращающий предварительно созданный тестовый набор:
 
<source lang="python">
def suite():
suite = unittest.TestSuite()
Строка 291 ⟶ 329 :
 
return unittest.TestSuite(map(WidgetTestCase, tests))
</source>
Поскольку создание подкласса TestCase с несколькими тестовыми функциями со сходными названиями является общей практикой, в unittest есть класс TestLoader, который можно использовать для автоматизации процесса создания тестового набора и наполнения его отдельными тестами. Например, строка:
 
Поскольку создание подкласса '''TestCase''' с несколькими тестовыми функциями со сходными названиями является общей практикой, в '''unittest''' есть класс '''TestLoader''', который можно использовать для автоматизации процесса создания тестового набора и наполнения его отдельными тестами. Например, строка:
 
<source lang="python">
suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
</source>
создаст тестовый набор выполняющий методы WidgetTestCase.test_default_size() и WidgetTestCase.test_resize(). TestLoader автоматически находит тестовые методы по префиксу 'test' в названии метода.
 
Обратите внимание, что порядок в котором будут выполняться тесты определяется сортировкой названий тестовых функций с помощью встроенной функции cmp().
создаст тестовый набор выполняющий методы '''WidgetTestCase.test_default_size()''' и '''WidgetTestCase.test_resize()'''. '''TestLoader''' автоматически находит тестовые методы по префиксу 'test' в названии метода.
Часто возникает необходимость группировать тестовые наборы, например для выполнения тестов для целой системы сразу. С этим нет никаких сложностей, поскольку экземпляры TestSuite могут добавляться в TestSuite, так же как и экземпляры TestCase.
Обратите внимание, что порядок в котором будут выполняться тесты определяется сортировкой названий тестовых функций с помощью встроенной функции '''cmp()'''.
Часто возникает необходимость группировать тестовые наборы, например для выполнения тестов для целой системы сразу. С этим нет никаких сложностей, поскольку экземпляры '''TestSuite''' могут добавляться в '''TestSuite''', так же как и экземпляры '''TestCase'''.
 
<source lang="python">
suite1 = module1.TheTestSuite()
suite2 = module2.TheTestSuite()
alltests = unittest.TestSuite([suite1, suite2])
</source>
Код тестовых вариантов и тестовых наборов может размещаться в тех же модулях, что и код, который они тестируют (таких как widget.py), но размещение тестового кода в отдельном модуле (таком как test_widget.py) дает ряд преимуществ:
 
• Тестовый модуль может выполняться отдельно, из командной строки;
Код тестовых вариантов и тестовых наборов может размещаться в тех же модулях, что и код, который они тестируют (таких как '''widget.py'''), но размещение тестового кода в отдельном модуле (таком как '''test_widget.py''') дает ряд преимуществ:
• Тестовый код проще отделять от поставляемого кода;
 
• Меньше искушение изменять тестовый код для большего соответствия тестируемуму коду, без веских оснований;
*Тестовый модуль может выполняться отдельно, из командной строки;
• Тестовый код следует изменять значительно реже, чем тестируемый код;
*Тестовый код проще отделять от поставляемого кода;
• Проще проводить рефакторинг тестового кода;
*Меньше искушение изменять тестовый код для большего соответствия тестируемуму коду, без веских оснований;
• Тесты для модулей написанных на C должен быть отдельных модулях в любом случае, так почему бы не быть последовательным?
*Тестовый код следует изменять значительно реже, чем тестируемый код;
• При изменении тестовой стратегии, не требуется изменять исходный код.
25.3.3.*Проще Повторноепроводить использование старогорефакторинг тестового кода;
*Тесты для модулей написанных на C должен быть отдельных модулях в любом случае, так почему бы не быть последовательным?
У некоторых пользователей может оказаться тестовый код, который они хотели бы выполнять из unittest без преобразования всех старых тестовых функций в подклассы TestCase.
*При изменении тестовой стратегии, не требуется изменять исходный код.
 
====Повторное использование старого тестового кода====
 
У некоторых пользователей может оказаться тестовый код, который они хотели бы выполнять из '''unittest''' без преобразования всех старых тестовых функций в подклассы '''TestCase'''.
По этой причине, в unittest включен класс FunctionTestCase. Этот подкласс TestCase может использоваться для оборачивания уже существующих тестовых функций. Также обеспечивается поддержка функций установки начальных значений и сброса.
Для следующей тестовой функции: