Тестирование
- Место тестирования в жизненном цикле программного продукта
- Собирается — значит, работает!
- + Проверяется анализатором кода, и потыкано end-user тестерами согласно тест-плану
- + Автоматическая проверка работоспособности там, где это возможно
- + Измерение тестового покрытия (coverage)
- Ручное, автоматизированное, автоматическое
- unit — спецификация компонентов (функций/классов и т. п.)
integration — спецификация интерфейсов между компонентами
- system — спецификация конечного продукта
- acceptacne — потребительские/рыночные/эксплутационные/… свойства продукта
- Дисциплина:
- Сначала весь код, потом некоторые тесты (когда это можно?)
- Каждая новая фича сопровождается тестом
Разработка_через_тестирование (TDD)
- сначала пишется тест и заглушка
сам код падает (иначе бесполезен)
- под тест пишется код
- код не падает
код изменяется и всё равно не падает
- green test trap: Тестирование может доказать наличие дефектов, но не их отсутствие
- red test trap: Не всякие проваленные тесты означают дефекты. Могут означать пробел в требованиях, в том числе нефункциональных
- Полезные ≠ друг другу термины:
ошибка программиста при написании программы может привести к
дефекту (багу) в программе, который в свою очередь может
проявиться (или не проявиться) в виде программного сбоя
- Стоимость исправления дефекта возрастает пропорционально его «возрасту»
- Непрерывная интеграция
Модульное тестирование в Python
Doctest
doctest: тест = диалог с python-интерпретатором
- Модуль
- Тестируем вручную:
- Добавляем тесты в docstring:
def moo(oos=2, end=""): '''Издать мычание длиной oos с end в конце Оба параметра необязательны: >>> moo() 'Moo' Первый задаёт количество букв 'o' в слове 'Moo' >>> moo(4) 'Mooooo' Букв 'o' может и не быть >>> moo(0) 'M' Второй задаёт символ после всех 'o' (по умолчанию — ничего) >>> moo(end='!') 'Moo!' >>> moo(0,'?') 'M?' ''' return "M"+"o"*oos+end
- Тестирование:
$ python3 -m doctest Moo.py ********************************************************************** File "/home/george/src/moo/Moo.py", line 13, in Moo.moo Failed example: moo(4) Expected: 'Mooooo' Got: 'Moooo' ********************************************************************** 1 items had failures: 1 of 5 in Moo.moo ***Test Failed*** 1 failures.
- Отчёт (с успешными тестами):
$ python3 -m doctest -v Moo.py Trying: moo() Expecting: 'Moo' ok Trying: moo(4) Expecting: 'Mooooo' ********************************************************************** File "/home/george/src/moo/Moo.py", line 13, in Moo.moo Failed example: moo(4) Expected: 'Mooooo' Got: 'Moooo' Trying: moo(0) Expecting: 'M' ok Trying: moo(end='!') Expecting: 'Moo!' ok Trying: moo(0,'?') Expecting: 'M?' ok 1 items had no tests: Moo ********************************************************************** 1 items had failures: 1 of 5 in Moo.moo 5 tests in 2 items. 4 passed and 1 failed. ***Test Failed*** 1 failures.
Тестирование исключений:
Как обычно, добавим просто весь вывод!
1 def moo(oos=2, end=""):
2 '''Издать мычание длиной oos с end в конце
3 ...
4
5 Здесь должно быть исключение:
6 >>> moo("QQ")
7 Traceback (most recent call last):
8 File "<stdin>", line 1, in <module>
9 File "/home/george/src/tests/Moo.py", line 33, in moo
10 return "M"+"o"*moos+end
11 TypeError: can't multiply sequence by non-int of type 'str'
12 '''
Вообще говоря, важны только три строчки, остальные можно выкинуть
... Здесь должно быть исключение: >>> moo("QQ") Traceback (most recent call last): TypeError: can't multiply sequence by non-int of type 'str' ...
Тесты должны пройти!
Перенос тестов во внешний файл
Пишем файл ( можно в .rst, для Sphinx), например, exttest.rst:
External test ============= Using Moo --------- Start from importing `Moo` module: >>> import Moo Then call `moo`: >>> Moo.moo(5) 'Mooooo'
К нему запускалку тестов:
И запускаем её (ключи как у модуля pytest):
$ python3 exttest.py -v Trying: import Moo Expecting nothing ok Trying: Moo.moo(5) Expecting: 'Mooooo' ok 1 items passed all tests: 2 tests in exttest.rst 2 tests in 1 items. 2 passed and 0 failed. Test passed.
«Серьёзные» фреймворки
TODO
- unittest
- py.test , nose
- Показатель покрытия кода тестами (coverage)
TODO pytest/cov
Д/З
- Осознать, что нуждается в unit-тестировании
- Оснастить код семестрового проекта unit-тестами (любой фреймворк)
- Зафиксировать в документации, как их запускать
- Подумать над тестированием UI
например, с помощью порождения событий tkinter