Фреймворк pytest позволяет легко писать небольшие, удобочитаемые тесты и может масштабироваться для поддержки сложного функционального тестирования приложений и библиотек.
Для работы pytest требуется: Python 3.7+ или PyPy3.
Установка модуля pytest в виртуальное окружение.
Модуль pytest размещен на PyPI, поэтому установка относительно проста.
# создаем виртуальное окружение, если нет $ python3 -m venv .venv —prompt VirtualEnv # активируем виртуальное окружение $ source .venv/bin/activate # обновляем `pip` (VirtualEnv):~$ python3 -m pip install -U pip # ставим модуль `pytest` (VirtualEnv):~$ python3 -m pip install -U pytest
Общие сведения о тестировании при помощи pytest
Тесты предназначены для того, чтобы посмотреть на результат поведения кода в определенной ситуации и убедиться, что результат соответствует тому, который ожидается. Поведение нельзя измерить эмпирически, поэтому написание тестов может быть сложной задачей.
Поведение — это то, как некоторая система действует в ответ на конкретную ситуацию и/или действия. Но как именно и почему что-то делается, не так важно, как то, что было сделано.
Распознавание лиц на Python | Определение возраста, эмоций и расы по фотографии лица
Можно думать о тесте как о четырех этапах:
- Подготовка.
- Действие.
- Утверждение.
- Очистка.
Подготовка.
ПОДГОТОВКА означает практически все, кроме ДЕЙСТВИЯ. Проще говоря — это подготовка окружения и объектов, которые требуются для проведения ДЕЙСТВИЯ. Это может быть все что угодно: подготовка к инициализации объектов, создание временных каталогов и файлов, запуск/остановку служб, ввод записей в базу данных или даже такие вещи, как определение URL-адреса для запроса, создание некоторых учетных данных для еще не существующего пользователя или просто ожидание завершения какого-либо процесса.
Действие.
ДЕЙСТВИЕ это то, что изменит состояние, которое запускает поведение, подлежащее тестированию. Именно это поведение осуществляет изменение тестируемой системы (SUT) по которому можно судить об устойчивости этой системы. Обычно ДЕЙСТВИЕ принимает форму вызова функции/метода с ПОДГОТОВЛЕННЫМ контекстом для моделирования той или иной ситуации.
Утверждение.
УТВЕРЖДЕНИЕ — это место, где проверяется результирующее состояние тестируемой системы, то есть, что соответствует или не соответствует ожидаемому результату/поведению. Утверждение в тесте — это проведение сравнения полученного состояния во время ДЕЙСТВИЯ с неким, наперед известным/ожидаемым результатом, на основании которого можно судить об устойчивости системы. Например: если итоговая переменная result должна иметь список целых чисел в диапазоне от 0 до 10, то утверждение будет выглядеть следующим образом:
# длинные утверждения не дают сразу понять, # что же здесь хотели проверить assert (isinstance(result, list) and all([isinstance(n, int) for n in result]) and all([0 n 10 for n in result]))
Что бы не писать длинные утверждения, их можно разнести по разным тестовым функциям, которые в свою очередь можно объединить в группу при помощи класса или модуля. Группы тестов помогают использовать одни и те же значения переменных для тестируемой функции/метода. Это поможет избежать передачу параметров для каждого отдельного теста, при тестировании одной и той же функции.
Вычисляем по IP с помощью Python | Как определить местоположение по IP
Очистка.
ОЧИСТКА — это то, где тест сам по себе завершает работу, и что бы случайно не повлиять на итоги других тестов, производится очистка ПОДГОТОВЛЕННОГО контекста (если это требуется). Например, удаление тестовых пользователей/записей из баз данных, очистка временных файлов из рабочей директории и т.д.
Очистку можно производить как в самих тестовых функциях, так и в фикстурах.
По своей сути, тест — это этапы ДЕЙСТВИЯ и УТВЕРЖДЕНИЯ, а этап ПОДГОТОВКИ обеспечивает только контекст. Поведение существует между ДЕЙСТВИЕМ и УТВЕРЖДЕНИЕМ.
Общий примеры использования фреймворка pytest .
На базовом уровне, тестовые функции запрашивают необходимые им фикстуры, объявляя их в качестве аргументов.
Когда pytest запускает тест, он просматривает аргументы в сигнатуре этой тестовой функции, а затем ищет фикстуры с теми же именами, что и эти аргументы. Как только pytest находит их, он запускает эти фикстуры, фиксирует то, что они вернули (если возвращают), и передает эти объекты в тестовую функцию в качестве аргументов.
В этом примере test_fruit_salad() «запрашивает» fruit_bowl() (т.е. def test_fruit_salad(fruit_bowl): ), и когда pytest увидит это, он выполнит функцию-фикстуру fruit_bowl и передаст возвращаемый объект в test_fruit_salad в качестве аргумента fruit_bowl .
А так одна фикстура, может запрашивать/взаимодействовать с другой фикстурой:
Передача параметров в тестируемый метод/функцию.
Фреймворк pytest позволяет параметризировать тест на нескольких уровнях:
Для этого создадим необходимые папки и файлы:
# создаем необходимые папки $ mkdir -p ./testing/test_folder # создаем необходимые файлы (они будут пустые) $ touch ./testing/util.py ./testing/test_folder/test_util.py
Далее открываем файл ./testing/util.py и вставляем в него код ниже.
# файл ./testing/util.py def str_to_num(str): «»»Вспомогательная функция: преобразует строку в число»»» if ‘.’ in str and str.replace(‘.’, »).isdigit(): return float(str) elif str.isdigit(): return int(str) def str_to_int_list(str_lst): «»»Тестируемая функция: преобразует список строк в список целых чисел»»» num_list = [] for item in str_lst: n = str_to_num(item) if n is not None: if isinstance(n, float): n = round(n) if 0 n 10: num_list.append(n) return num_list
И наконец открываем файл ./test_folder/test_util.py и вставляем в него код с тестом, который расположен ниже.
Обратите внимание как в файле теста происходит импорт from util import str_to_int_list , хотя файл util.py находится на уровень выше. (подробнее об импорте при тестировании в материале «Интеграция тестов pytest с проектом.»)
Запускаем тесты. Для этого активируем виртуальное окружение, где установлен pytest и затем перейдем в папку ./testing :
# активируем виртуальное окружение $ source .venv/bin/activate # перейдем в папку `./testing` (VirtualEnv) :~$ cd testing # запускаем тест (VirtualEnv) :~/testing$ python3 -m pytest -v
Готово, тесты должны отработать.
Источник: docs-python.ru
Тестируем на Python: unittest и pytest. Инструкция для начинающих
Меня зовут Андрей Смирнов, я занимаюсь Python-разработкой, автоматизацией технических процессов и преподаю промышленное программирование в Школе программистов МШП.
Не секрет, что разработчики создают программы, которые рано или поздно становятся очень масштабными (если смотреть на количество строчек кода). А с этим приходит и большая ответственность за качество.
Сейчас расскажу, как unittest и pytest помогут найти ошибки в программах и исключить их в будущем.
Итак, тестирование
Каждый, кто писал первые программы (будь то классический «hello, world» или же калькулятор), всегда запускал тесты, чтобы проверить их работу.
Сам факт запуска — самое первое, незримое касание технологии тестирования в вашей жизни. Рассмотрим его как процесс поиска ошибок на чуть более сложной программе.
Например, вам нужно ввести три числа (a, b, c) и найти корни квадратного уравнения. Для решения пишем код:
from math import sqrt
def square_eq_solver(a, b, c):
result = []
discriminant = b * b — 4 * a * c
if discriminant == 0:
result.append(-b / (2 * a))
else:
result.append((-b + sqrt(discriminant)) / (2 * a))
result.append((-b — sqrt(discriminant)) / (2 * a))
def show_result(data):
if len(data) > 0:
for index, value in enumerate(data):
print(f’Корень номер равен ‘)
else:
print(‘Уравнение с заданными параметрами не имеет корней’)
def main():
a, b, c = map(int, input(‘Пожалуйста, введите три числа через пробел: ‘).split())
result = square_eq_solver(a, b, c)
show_result(result)
if __name__ == ‘__main__’:
main()
Сразу оговорюсь: любую задачу, какой бы она ни была краткой, я рассматриваю с позиции «когда-нибудь она вырастет и станет очень объёмной». Поэтому всегда стараюсь разделять программу на различные подпрограммы (ввод/обработка/вывод).
Возможно, вы уже заметили ошибку в коде. Однако иногда она может быть скрыта настолько глубоко, что её просто так не обнаружишь. И в таком случае единственный способ вывести ее на свет — протестировать код. Как это сделать?
— зная алгоритм нахождения корней уравнения, определяем наборы входных данных, которые будут переданы на вход программе;
— зная входные данные, можно вручную просчитать, какой ответ должна дать программа;
— запускаем программу и передаем ей на вход исходные данные;
— получаем от нее ответ и сравниваем с тем, который должен быть получен. Если они совпадают — хорошо, идём к следующему набору данных, если нет, сообщаем об ошибке.
Например, для данной задачи можно подобрать следующие тесты:
- 10x**2 = 0 — единственный корень x=0
- 2x**2 + 5x — 3 = 0 — у такого уравнения два корня (x1 = 0.5, x2=-3)
- 10x**2+2 = 0 — у этого уравнения корней нет
Тесты подобрали, что дальше? Правильно, запускаем:
Тест номер 1
> python.exe example.py
Пожалуйста, введите три числа через пробел: 10 0 0
Корень номер 0 равен 0.00
Тест номер 2:
> python.exe example.py
Пожалуйста, введите три числа через пробел: 2 5 -3
Корень номер 1 равен 0.50
Корень номер 2 равен -3.00
Тест номер 3:
> python.exe example.py
Пожалуйста, введите три числа через пробел: 10 0 2
Traceback (most recent call last):
File «C:PyProjectstprogerexample.py», line 32, in
main()
File «C:PyProjectstprogerexample.py», line 27, in main
result = square_eq_solver(a, b, c)
File «C:PyProjectstprogerexample.py», line 11, in square_eq_solver
result.append((-b + sqrt(discriminant)) / (2 * a))
ValueError: math domain error
Упс… В третьем тесте произошла ошибка. Как раз та, которую вы могли заметить в исходном коде программы — не обрабатывался случай с нулевым дискриминантом. В итоге, можно подкорректировать код функции так, чтобы этот вариант обрабатывался правильно:
def square_eq_solver(a, b, c):
result = []
discriminant = b * b — 4 * a * c
if discriminant == 0:
result.append(-b / (2 * a))
elif discriminant > 0: # # при нулевом дискриминанте
# не будут вычисляться корни
result.append((-b + sqrt(discriminant)) / (2 * a))
result.append((-b — sqrt(discriminant)) / (2 * a))
Запускаем все тесты повторно и они срабатывают нормально.
Но учтите, чтобы повторно проверить программу, потребуется потратить несколько минут и снова проверить все три варианта входных значений. Если таких вариантов будет много, вызывать их вручную будет очень накладно. И здесь на сцену выходит автоматизированное тестирование.
Программа автоматического тестирования запускается на основе заранее заготовленных входных/выходных данных и программы, которая будет их вызывать. По сути, это программа, тестирующая другие программы. И в рамках экосистемы языка Python есть несколько пакетов, позволяющих автоматизировать процесс тестирования.
Unittest и pytest: пишем тесты
Две самые популярные библиотеки — unittest и pytest. Попробуем каждую, чтобы объективно оценить синтаксис.
Начнем с unittest, потому что именно с нее многие знакомятся с миром тестирования. Причина проста: библиотека по умолчанию встроена в стандартную библиотеку языка Python.
Формат кода
По формату написания тестов она сильно напоминает библиотеку JUnit, используемую в языке Java для написания тестов:
- тесты должны быть написаны в классе;
- класс должен быть отнаследован от базового класса unittest.TestCase;
- имена всех функций, являющихся тестами, должны начинаться с ключевого слова test;
- внутри функций должны быть вызовы операторов сравнения (assertX) — именно они будут проверять наши полученные значения на соответствие заявленным.
Пример использования unittest для нашей задачи
class SquareEqSolverTestCase(unittest.TestCase):
def test_no_root(self):
res = square_eq_solver(10, 0, 2)
self.assertEqual(len(res), 0)
def test_single_root(self):
res = square_eq_solver(10, 0, 0)
self.assertEqual(len(res), 1)
self.assertEqual(res, [0])
def test_multiple_root(self):
res = square_eq_solver(2, 5, -3)
self.assertEqual(len(res), 2)
self.assertEqual(res, [0.5, -3])
Запускается данный код следующей командой
python.exe -m unittest example.py
И в результате на экран будет выведено:
В случае, если в каком-нибудь из тестов будет обнаружена ошибка, unittest не замедлит о ней сообщить:
Unittest: аргументы “за”
- Является частью стандартной библиотеки языка Python: не нужно устанавливать ничего дополнительно;
- Гибкая структура и условия запуска тестов. Для каждого теста можно назначить теги, в соответствии с которыми будем запускаться либо одна, либо другая группа тестов;
- Быстрая генерация отчетов о проведенном тестировании, как в формате plaintext, так и в формате XML.
Unittest: аргументы “против”
- Для проведения тестирования придётся написать достаточно большое количество кода (по сравнению с другими библиотеками);
- Из-за того, что разработчики вдохновлялись форматом библиотеки JUnit, названия основных функций написаны в стиле camelCase (например setUp и assertEqual);
- В языке python согласно рекомендациям pep8 должен использоваться формат названий snake_case (например set_up и assert_equal).
Pytest
Возможно, наиболее популярный фреймворк с открытым исходным кодом из всех, представленных здесь.
Pytest позволяет провести модульное тестирование (тестирование отдельных компонентов программы), функциональное тестирование (тестирование способности кода удовлетворять бизнес-требования), тестирование API (application programming interface) и многое другое.
Формат кода
Написание тестов здесь намного проще, нежели в unittest. Вам нужно просто написать несколько функций, удовлетворяющих следующим условиям:
- Название функции должно начинаться с ключевого слова test;
- Внутри функции должно проверяться логическое выражение при помощи оператора assert.
Пример использования pytest для нашей задачи:
def test_no_root():
res = square_eq_solver(10, 0, 2)
assert len(res) == 0
def test_single_root():
res = square_eq_solver(10, 0, 0)
assert len(res) == 1
assert res == [0]
def test_multiple_root():
res = square_eq_solver(2, 5, -3)
assert len(res) == 3
assert res == [0.5, -3]
Запускается данный код следующей командой
pytest.exe example.py
И в результате на экран будет выведено:
> pytest.exe example.py
======================= test session starts ======================
platform win32 — Python 3.9.6, pytest-7.1.2, pluggy-1.0.0
rootdir: C:PyProjectstproger
collected 3 items
В случае ошибки вывод будет несколько больше:
> pytest.exe example.py
======================= test session starts ======================
platform win32 — Python 3.9.6, pytest-7.1.2, pluggy-1.0.0
rootdir: C:PyProjectstproger
collected 3 items
example.py ..F [100%]
============================ FAILURES ============================
_______________________ test_multiple_root _______________________
def test_multiple_root():
res = square_eq_solver(2, 5, -3)
> assert len(res) == 3
E assert 2 == 3
E + where 2 = len([0.5, -3.0])
example.py:116: AssertionError
===================== short test summary info ====================
FAILED example.py::test_multiple_root — assert 2 == 3
Pytest: аргументы “за”
- Позволяет писать компактные (по сравнению с unittest) наборы тестов;
- В случае возникновения ошибок выводится гораздо больше информации о них;
- Позволяет запускать тесты, написанные для других тестирующих систем;
- Имеет систему плагинов (и сотни этих самых плагинов), расширяющую возможности фреймворка. Примеры таких плагинов: pytest-cov , pytest-django , pytest-bdd ;
- Позволяет запускать тесты в параллели (при помощи плагина pytest-xdist ).
Pytest: аргументы “против”
- pytest не входит в стандартную библиотеку языка Python. Поэтому его придётся устанавливать отдельно при помощи команды pip install pytest;
- совместимость кода с другими фреймворками отсутствует. Так что, если напишете код под pytest, запустить его при помощи встроенного unittest не получится.
Ну и что лучше?
- Если вам нужно базовое юнит-тестирование и вы знакомы с фреймворками вида xUnit, тогда вам подойдёт unittest.
- Если нужен фреймворк, позволяющий создавать краткие и изящные тесты, реализующие сложную логику проверок, то pytest.
Post Scriptum
Тема контроля качества очень обширна. И даже к написанному мной коду очень легко придраться. Как минимум здесь отсутствует проверка на то, что вводимые данные обязательно должны быть целыми числами. Если ввести любое другое число или даже строку, она обязательно завершится с ошибкой.
Кстати, в этой программе я намеренно оставил ещё одну ошибку (на сей раз уже логическую), связанную с нахождением корня. Напишите в комментариях, с чем она может быть связана, и какой тест поможет её отловить
Источник: dzen.ru
Home
Python – потрясающий язык программирования. Его любят и новички, и эксперты, и он регулярно оценивается как язык с самым высоким спросом. На конференции PyData Carolinas 2016 Джош Хоус, старший менеджер управления данными в MaxPoint, описал Python примерно так:
Python – магический инструмент, который позволяет легко решать самые сложные мировые проблемы.
Я впервые столкнулся с Python в старшей школе более десяти лет назад, но начал по-настоящему пользоваться им и полюбил только недавно, используя его для тест-автоматизации. Эта серия статей научит вас тестировать на Python. Эта вводная статья даст вам базовые ориентиры, а каждая последующая подробно разберет один из фреймворков Python.
Почему надо тестировать именно на Python?
Как упоминалось в другой статье, «Лучший язык программирования для автоматизации», Python внятен, элегантен и читабелен – это именно то, что нужно для превращения тест-кейсов в тест-скрипты. Его хорошо поддерживаемые тест-пакеты отлично управляются и с белым, и с черным ящиком. Он также дружит с командной строкой. Инженеры, никогда не использовавшие Python, обычно быстро ему обучаются.
Примеры ниже иллюстрируют способы использования Python для тест-автоматизации:
- Разработчик внедряет быстрые проверки в функцию docstrings.
- Разработчик пишет юнит-тесты для модуля или пакета.
- Тестировщик пишет интеграционные тесты для REST API.
- Тестировщик пишет end-to-end-тесты, используя Selenium.
- Дата-аналитик проверяет функции в блокноте Jupyter.
- «Три товарища» пишут сценарии «Если-Когда-Тогда» для BDD-тестирования.
Помните, Python можно использовать для любого черноящичного тестирования, даже если ваш продукт не написан на Python!
Версия Python
- Используйте подходящую версию Python для тестов белого ящика.
- Используйте CPython 3 для черного ящика, если это возможно.
В этой серии статей, если не указано иное, используется CPython 3.
Выбор фреймворка
Фреймворков Python так много, что выбор кажется устрашающим – только посмотрите на Python wiki, The Hitchhiker’s Guide to Python и pythontesting.net. Несмотря на избыток вариантов, надо учитывать несколько важных моментов.
- Учитывайте тип тестирования. С базовыми юнит-тестами справятся unittest и даже doctest, но более высокоуровневое тестирование лучше сработает с другими фреймворками вроде pytest. BDD-тестирование потребует behave, lettuce, или radish.
- Учитывайте поддерживаемую версиюPython.Python 2 и 3 – это два разных языка. У разных фреймворков разный уровень поддержки версий, что особенно проблематично для тестирования белого ящика. Более того, функциональность в зависимости от версии Python тоже может разливаться.
- Подумайте о поддержке и развитии. Как правило, лучше выбирать зрелые, активно развивающиеся фреймворки для будущей поддержки. К примеру, когда-то популярный nose приказал долго жить.
Следующие статьи серии детально расскажут о разных фреймворках, чтобы вы уверенно выбрали наилучший для ваших нужд вариант.
Виртуальные окружения
Виртуальное окружение (VE) – это локальная инсталляция Python с определенным набором пакетов. Инструменты вроде venv (Python 3.3+), virtualenv (Python 2 и 3), и Conda (Python 2 и 3; для дата-инженеров) помогают легко создать виртуальные окружения из командной строки. Pipenv идет дальше, комбинируя управление VE с простым, но мощным управлением пакетами. Создание как минимум одного отдельного VE для каждого проекта Python – как правило, хорошая практика. VE очень полезны для автоматизации, потому что:
Они позволяют инженерам одновременно поддерживать несколько Python-окружений.
- Инженеры могут разрабатывать и тестировать пакеты на обеих версиях Python.
- Инженеры могут разделять проекты, опирающиеся на разные версии пакетов.
Они позволяют пользователям локально устанавливать пакеты Python, не меняя глобальные установки.
- У пользователей может не быть прав устанавливать пакеты глобально.
- Глобальные изменения могут повредить другому Python-зависимому ПО.
Они могут импортировать и экспортировать списки пакетов для легкого восстановления.
VE особенно ценны при непрерывной интеграции и деплое, потому что легко обеспечивают согласованность Python. К примеру, задача в Jenkins может создать виртуальное окружение, установить в него зависимости из PyPI, прогнать тесты Python, и безопасно самоуничтожиться. Как только продукт готов к деплою, можно воспользоваться той же самой конфигурацией VE.
Рекомендованные IDE
Любая серьезная автоматизация требует не менее серьезной IDE. Мой фаворит — JetBrains PyCharm. Мне очень нравится его изящный интерфейс и интуитивная суть, и он сразу поддерживает несколько тест-фреймворков Python. PyCharm можно скачать как отдельную IDE или как плагин к JetBrains IntelliJ IDEA. Community Edition бесплатна и удовлетворяет большинство потребностей автоматизации, а Professional Edition требует лицензии.
PyDev – хорошая альтернатива для предпочитающих Eclipse. Eric подойдет пуристам – это Python-IDE, написанная на Python. У всех трех есть фреймворк плагинов, но PyCharm и PyDev выигрывают по популярности и поддержке. Есть также классическая IDLE, но ее использование сейчас не рекомендуется из-за багов и наличия более удачных вариантов.
Легкие текстовые редакторы позволят просто и быстро вносить небольшие правки. Из недавних фаворитов — Visual Studio Code. Notepad++ всегда выигрывает для Windows. Atom – более новый кросс-платформенный редактор от GitHub, набирающий популярность. Конечно, UNIX-платформы обычно предоставляют vim или emacs.
Обзор фреймворков
Если эта серия статей для вас, установите IDE, настройте виртуальное окружение, и поехали! Следующие статьи расскажут о популярных тест-фреймворках Python. Каждую из них можно использовать в качестве стартовой информации или быстрой справки. Пожалуйста, обратитесь к официальной документации фреймворка для подробной информации – нет смысла дублировать ее здесь.
Источник: www.software-testing.ru