0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<h2>Введение</h2>
1
<h2>Введение</h2>
2
<blockquote><p>Этим уроком открывается цикл практических занятий по написанию тестов с применением различных инструментов.</p>
2
<blockquote><p>Этим уроком открывается цикл практических занятий по написанию тестов с применением различных инструментов.</p>
3
</blockquote><p>unittest - инструмент для тестирования в Python. Это стандартный модуль для написания юнит-тестов на Python. Unittest это порт JUnit с Java. Иными словами, и в коде модуля, и при написании тестов легко прослеживается ООП стиль, что весьма удобно для тестирования процедур и классов.</p>
3
</blockquote><p>unittest - инструмент для тестирования в Python. Это стандартный модуль для написания юнит-тестов на Python. Unittest это порт JUnit с Java. Иными словами, и в коде модуля, и при написании тестов легко прослеживается ООП стиль, что весьма удобно для тестирования процедур и классов.</p>
4
<p>Документация доступна по следующим ссылкам:<a>python3</a>,<a>python2</a></p>
4
<p>Документация доступна по следующим ссылкам:<a>python3</a>,<a>python2</a></p>
5
<p>В данном инструменте много возможностей: проверки (<strong>assert</strong>*), декораторы, позволяющие пропустить отдельный тест (<strong>@skip</strong>,<strong>@skipIf</strong>) или обозначить сломанные тесты (<strong>@expectedFailure</strong>) и этим не заканчивается список. Использование assert'ов с лихвой покрывает нужды при написании тестов.</p>
5
<p>В данном инструменте много возможностей: проверки (<strong>assert</strong>*), декораторы, позволяющие пропустить отдельный тест (<strong>@skip</strong>,<strong>@skipIf</strong>) или обозначить сломанные тесты (<strong>@expectedFailure</strong>) и этим не заканчивается список. Использование assert'ов с лихвой покрывает нужды при написании тестов.</p>
6
<h2>Описание unittest</h2>
6
<h2>Описание unittest</h2>
7
<p>Полезная черта unittest - автоматизированное тестирование. Есть и другие:</p>
7
<p>Полезная черта unittest - автоматизированное тестирование. Есть и другие:</p>
8
<ul><li>можно собирать тесты в группы</li>
8
<ul><li>можно собирать тесты в группы</li>
9
<li>собирать результаты выполнения тестов (например, для отчета)</li>
9
<li>собирать результаты выполнения тестов (например, для отчета)</li>
10
<li>ООП стиль позволяет уменьшить дублирование кода при схожих объектах тестирования</li>
10
<li>ООП стиль позволяет уменьшить дублирование кода при схожих объектах тестирования</li>
11
</ul><p>В использовании unittest присутствуют несколько концепций</p>
11
</ul><p>В использовании unittest присутствуют несколько концепций</p>
12
<h3>test case</h3>
12
<h3>test case</h3>
13
<p>test case -это наименьшая единица тестирования. Он проверяет конкретный ответ для конкретного набора входных данных.</p>
13
<p>test case -это наименьшая единица тестирования. Он проверяет конкретный ответ для конкретного набора входных данных.</p>
14
<h3>test suite</h3>
14
<h3>test suite</h3>
15
<p>test suite представляет собой сборник тестовых случаев, тестовых наборов. Используется для агрегирования тестов, которые должны выполняться вместе.</p>
15
<p>test suite представляет собой сборник тестовых случаев, тестовых наборов. Используется для агрегирования тестов, которые должны выполняться вместе.</p>
16
<h3>test fixture</h3>
16
<h3>test fixture</h3>
17
<p>test fixture - это фиксированное состояние объектов используемых в качестве исходного при выполнении тестов.</p>
17
<p>test fixture - это фиксированное состояние объектов используемых в качестве исходного при выполнении тестов.</p>
18
<p>Цель использования fixture - если у вас сложный test case, то подготовка нужного состояния легко может занимать много ресурсов (например, вы считаете функцию с определенной точностью и каждый следующий знак точности в расчетах занимает день). Используя fixture (на сленге - фикстуры) предварительную подготовку состояния пропускаем и сразу приступаем к тестированию.</p>
18
<p>Цель использования fixture - если у вас сложный test case, то подготовка нужного состояния легко может занимать много ресурсов (например, вы считаете функцию с определенной точностью и каждый следующий знак точности в расчетах занимает день). Используя fixture (на сленге - фикстуры) предварительную подготовку состояния пропускаем и сразу приступаем к тестированию.</p>
19
<p>Test fixture может выступать, например, в виде:</p>
19
<p>Test fixture может выступать, например, в виде:</p>
20
<ul><li>состояние базы данных</li>
20
<ul><li>состояние базы данных</li>
21
<li>набор переменных среды</li>
21
<li>набор переменных среды</li>
22
<li>набор файлов с необходимым содержанием.</li>
22
<li>набор файлов с необходимым содержанием.</li>
23
</ul><h3>test runner</h3>
23
</ul><h3>test runner</h3>
24
<p>test runner - это компонент, который организует выполнение тестов и предоставляет результат пользователю.</p>
24
<p>test runner - это компонент, который организует выполнение тестов и предоставляет результат пользователю.</p>
25
<h2>Рекомендации к написанию тестов</h2>
25
<h2>Рекомендации к написанию тестов</h2>
26
<p>При написании тестов следует исходить из следующих принципов:</p>
26
<p>При написании тестов следует исходить из следующих принципов:</p>
27
<ul><li>Работа теста не должна зависеть от результатов работы других тестов.</li>
27
<ul><li>Работа теста не должна зависеть от результатов работы других тестов.</li>
28
<li>Тест должен использовать данные, специально для него подготовленные, и никакие другие.</li>
28
<li>Тест должен использовать данные, специально для него подготовленные, и никакие другие.</li>
29
<li>Тест не должен требовать ввода от пользователя</li>
29
<li>Тест не должен требовать ввода от пользователя</li>
30
<li>Тесты не должны перекрывать друг друга (не надо писать одинаковые тесты 20 раз). Можно писать частично перекрывающие тесты.</li>
30
<li>Тесты не должны перекрывать друг друга (не надо писать одинаковые тесты 20 раз). Можно писать частично перекрывающие тесты.</li>
31
<li>Нашел баг -> напиши тест</li>
31
<li>Нашел баг -> напиши тест</li>
32
<li>Тесты надо поддерживать в рабочем состоянии</li>
32
<li>Тесты надо поддерживать в рабочем состоянии</li>
33
<li>Модульные тесты не должны проверять производительность сущности (класса, функции)</li>
33
<li>Модульные тесты не должны проверять производительность сущности (класса, функции)</li>
34
<li>Тесты должны проверять не только то, что сущность работает корректно на корректных данных, но и то что ведет себя адекватно при некорректных данных.</li>
34
<li>Тесты должны проверять не только то, что сущность работает корректно на корректных данных, но и то что ведет себя адекватно при некорректных данных.</li>
35
<li>Тесты надо запускать регулярно</li>
35
<li>Тесты надо запускать регулярно</li>
36
</ul><h2>Практика</h2>
36
</ul><h2>Практика</h2>
37
<p>К написанию тестов стоит относится также как и к основному коду.</p>
37
<p>К написанию тестов стоит относится также как и к основному коду.</p>
38
<p>Написание тестов является хорошей инвестицией в будущее программы:</p>
38
<p>Написание тестов является хорошей инвестицией в будущее программы:</p>
39
<ul><li>Когда ваша программа становится настолько большой, что не помещается целиком у вас в голове, то это отличный звоночек, что стоит покрывать все тестами</li>
39
<ul><li>Когда ваша программа становится настолько большой, что не помещается целиком у вас в голове, то это отличный звоночек, что стоит покрывать все тестами</li>
40
<li>Если сейчас ваша программа не испытывает проблем, то через какое-то время библиотеки, которые вы используете, могут начать обновляться без обратной совместимости. Вот здесь-то тесты помогут</li>
40
<li>Если сейчас ваша программа не испытывает проблем, то через какое-то время библиотеки, которые вы используете, могут начать обновляться без обратной совместимости. Вот здесь-то тесты помогут</li>
41
<li>Когда вы занимаетесь рефакторингом кода - тесты помогут не сломать лишнего</li>
41
<li>Когда вы занимаетесь рефакторингом кода - тесты помогут не сломать лишнего</li>
42
</ul><p>Есть и другие причины писать тесты. В целом, практика показывает, что до тестов надо дорасти - в какой-то момент приходит понимание зачем же тратить на них время.</p>
42
</ul><p>Есть и другие причины писать тесты. В целом, практика показывает, что до тестов надо дорасти - в какой-то момент приходит понимание зачем же тратить на них время.</p>
43
<p>В качестве примеров использования unittest продемонстрирую и опишу основные возможности модуля. На мой взгляд это те 20% которые помогут сделать 80% результата.</p>
43
<p>В качестве примеров использования unittest продемонстрирую и опишу основные возможности модуля. На мой взгляд это те 20% которые помогут сделать 80% результата.</p>
44
<h3>Пример синтаксиса №1</h3>
44
<h3>Пример синтаксиса №1</h3>
45
<p>Рассмотрим следующий код:</p>
45
<p>Рассмотрим следующий код:</p>
46
<p>В данном примере показан общий шаблон для большинства тестов - здесь и наследование от TestCase, здесь и два простых теста, а также перегрузка встроенных в TestCase методов:</p>
46
<p>В данном примере показан общий шаблон для большинства тестов - здесь и наследование от TestCase, здесь и два простых теста, а также перегрузка встроенных в TestCase методов:</p>
47
<ul><li>Метод def setUp(self) вызывается<em>ПЕРЕД</em>каждым тестом.</li>
47
<ul><li>Метод def setUp(self) вызывается<em>ПЕРЕД</em>каждым тестом.</li>
48
<li>Метод def tearDown(self) вызывается<em>ПОСЛЕ</em>каждого теста</li>
48
<li>Метод def tearDown(self) вызывается<em>ПОСЛЕ</em>каждого теста</li>
49
</ul><p>Список подобных готовых функций такой:</p>
49
</ul><p>Список подобных готовых функций такой:</p>
50
<ul><li>setUp - подготовка прогона теста; вызывается перед каждым тестом.</li>
50
<ul><li>setUp - подготовка прогона теста; вызывается перед каждым тестом.</li>
51
<li>tearDown - вызывается после того, как тест был запущен и результат записан. Метод запускается даже в случае исключения (exception) в теле теста.</li>
51
<li>tearDown - вызывается после того, как тест был запущен и результат записан. Метод запускается даже в случае исключения (exception) в теле теста.</li>
52
<li>setUpClass - метод вызывается перед запуском всех тестов класса.</li>
52
<li>setUpClass - метод вызывается перед запуском всех тестов класса.</li>
53
<li>tearDownClass - вызывается после прогона всех тестов класса.</li>
53
<li>tearDownClass - вызывается после прогона всех тестов класса.</li>
54
<li>setUpModule - вызывается перед запуском всех классов модуля.</li>
54
<li>setUpModule - вызывается перед запуском всех классов модуля.</li>
55
<li>tearDownModule - вызывается после прогона всех тестов модуля.</li>
55
<li>tearDownModule - вызывается после прогона всех тестов модуля.</li>
56
</ul><p>Если запустить скрипт:</p>
56
</ul><p>Если запустить скрипт:</p>
57
<p>То получим:</p>
57
<p>То получим:</p>
58
<p>Теперь мы знаем как писать тест, как запускать. Перейдем к освещению вариантов использования тестов.</p>
58
<p>Теперь мы знаем как писать тест, как запускать. Перейдем к освещению вариантов использования тестов.</p>
59
<h3>Пример синтаксиса №2 - №...</h3>
59
<h3>Пример синтаксиса №2 - №...</h3>
60
<p>Прямо в док-строках описал когда выбрасывают ошибки эти проверки. В примере показаны как новые названия, так и их старые аналоги (в старых проектах такие еще встречаются)</p>
60
<p>Прямо в док-строках описал когда выбрасывают ошибки эти проверки. В примере показаны как новые названия, так и их старые аналоги (в старых проектах такие еще встречаются)</p>
61
<h3>Практические примеры</h3>
61
<h3>Практические примеры</h3>
62
<p>Перейдем к более практическим примерам. На них и рассмотрим еще некоторые важные способности unittest</p>
62
<p>Перейдем к более практическим примерам. На них и рассмотрим еще некоторые важные способности unittest</p>
63
<ul><li>Как тестировать конструктор?</li>
63
<ul><li>Как тестировать конструктор?</li>
64
<li>Как тестировать структуры данных?</li>
64
<li>Как тестировать структуры данных?</li>
65
<li>Как тестировать работу с БД?</li>
65
<li>Как тестировать работу с БД?</li>
66
</ul><h5><strong>Как тестировать конструктор?</strong></h5>
66
</ul><h5><strong>Как тестировать конструктор?</strong></h5>
67
<p>Приведу пример, как можно протестировать конструктор</p>
67
<p>Приведу пример, как можно протестировать конструктор</p>
68
<p>В коде видно, что есть класс, в конструкторе которого кидается исключение если значение аргумента не удовлетворяет условию. Это условие ловится функцией self.assertRaises или ее старым названием self.failUnlessRaises</p>
68
<p>В коде видно, что есть класс, в конструкторе которого кидается исключение если значение аргумента не удовлетворяет условию. Это условие ловится функцией self.assertRaises или ее старым названием self.failUnlessRaises</p>
69
<p>Во втором примере показан тест на количество аргументов в классе.</p>
69
<p>Во втором примере показан тест на количество аргументов в классе.</p>
70
<h5><strong>Как тестировать структуру данных?</strong></h5>
70
<h5><strong>Как тестировать структуру данных?</strong></h5>
71
<p>Продемонстрирую пример тестирования структуры данных. В данном случае есть класс Point, в котором хранится два float’а x,y. А дальше весь код такой же как и для других тестов.</p>
71
<p>Продемонстрирую пример тестирования структуры данных. В данном случае есть класс Point, в котором хранится два float’а x,y. А дальше весь код такой же как и для других тестов.</p>
72
<h5><strong>Как тестировать работу с БД?</strong></h5>
72
<h5><strong>Как тестировать работу с БД?</strong></h5>
73
<p>Примера под тестирование БД не приведу, однако, расскажу об общих соображениях.</p>
73
<p>Примера под тестирование БД не приведу, однако, расскажу об общих соображениях.</p>
74
<p>Работа с базой не так проста, как с обычной функцией, ведь база - это не просто программный код, база данных - это объект, сохраняющий своё состояние. И если мы начнём в процессе тестирования изменять данные в базе, то после каждого теста база будет изменяться. Это может помешать последующим тестам и необратимо испортить базу данных.</p>
74
<p>Работа с базой не так проста, как с обычной функцией, ведь база - это не просто программный код, база данных - это объект, сохраняющий своё состояние. И если мы начнём в процессе тестирования изменять данные в базе, то после каждого теста база будет изменяться. Это может помешать последующим тестам и необратимо испортить базу данных.</p>
75
<p>Ключ к решению проблемы - транзакции. Одна из особенностей этого механизма состоит в том, что до тех пор пока транзакция не завершена, вы всегда можете отменить все изменения и вернуть базу в состояние на момент начала транзакции. Алгоритм такой:</p>
75
<p>Ключ к решению проблемы - транзакции. Одна из особенностей этого механизма состоит в том, что до тех пор пока транзакция не завершена, вы всегда можете отменить все изменения и вернуть базу в состояние на момент начала транзакции. Алгоритм такой:</p>
76
<ol><li>открываем транзакцию;</li>
76
<ol><li>открываем транзакцию;</li>
77
<li>если нужно, выполняем подготовительные действия для тестирования;</li>
77
<li>если нужно, выполняем подготовительные действия для тестирования;</li>
78
<li>выполняем модульный тест (или просто запускаем сценарий, работу которого хотим проверить);</li>
78
<li>выполняем модульный тест (или просто запускаем сценарий, работу которого хотим проверить);</li>
79
<li>проверяем результат работы сценария;</li>
79
<li>проверяем результат работы сценария;</li>
80
<li>отменяем транзакцию, возвращая базу данных в исходное состояние.</li>
80
<li>отменяем транзакцию, возвращая базу данных в исходное состояние.</li>
81
</ol><p>Даже если в тестируемом коде останутся незакрытые транзакции, внешний ROLLBACK всё равно откатит все изменения корректно.</p>
81
</ol><p>Даже если в тестируемом коде останутся незакрытые транзакции, внешний ROLLBACK всё равно откатит все изменения корректно.</p>
82
<p>Помимо транзакций стоит обратить внимание, что тестирование следует делать на тестовой базе данных. Идеальным случаем будет полная инициализация окружения перед исполнением теста.</p>
82
<p>Помимо транзакций стоит обратить внимание, что тестирование следует делать на тестовой базе данных. Идеальным случаем будет полная инициализация окружения перед исполнением теста.</p>
83
<h2>Выводы</h2>
83
<h2>Выводы</h2>
84
<p>В уроке описаны базовые элементы работы с unittest. Не были затронуты статусы тестов, отчеты о тестировании. Однако, данной теории хватит для написания тестов. Давайте это сейчас и проверим, переходите к практике.</p>
84
<p>В уроке описаны базовые элементы работы с unittest. Не были затронуты статусы тестов, отчеты о тестировании. Однако, данной теории хватит для написания тестов. Давайте это сейчас и проверим, переходите к практике.</p>