HTML Diff
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>Нашел баг -&gt; напиши тест</li>
31 <li>Нашел баг -&gt; напиши тест</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>