PyTest

Contents
Introduction
Example
Структура проекта
Тестирование проверки аргументов на тип
Запуск unittest тестов
Добавить PyTest в PyCharm
Похожие статьи

Введение

Pytest поддерживает тесты, созданные с unittest .

Главное преимущество Pytest заключается в особенностях написания TestCase.

TestCase в pytest — это серия функций в файле Python, которые начинаются с имени test_.

У Pytest есть и другие отличительные особенности:

Написание теста TestSum в pytest выглядит так:

def test_sum(): assert sum([1, 2, 3]) == 6, "Should be 6" def test_sum_tuple(): assert sum((1, 2, 2)) == 6, "Should be 6"

Здесь удалены базовый класс TestCase и любое использование классов в принципе, а также точка входа с командной строки. Как обычно, дополнительная информация представлена на сайте Pytest.

Пример без IDE

Простейший пример в консоле Linux

Нужно подготовить окружение и создать два файла - с кодом и тестом для этого кода.

Файл с кодом можно назвать как угодно - желательно с маленькой буквы и именем несовпадающим с зарезервированными.

Файл с тестом обычно называют также но с префиксом test_ или реже постфиксом _test

mkdir /home/$(whoami)/python/pytest/qa
cd /home/$(whoami)/python/pytest/qa
python -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip
python -m pip install pytest
touch psum.py test_psum.py

В файле psum.py напишите простую функцию, например сложение

def add(x, y): return x + y

А в файле test_psum.py будет тест для этой функции

from psum import add def test_psum(): assert add(2, 3) == 5, "Should be 5"

Чтобы запустить тест нужно выполнить

pytest test_psum.py

================================================= test session starts ================================================== platform linux -- Python 3.9.5, pytest-7.0.1, pluggy-1.0.0 rootdir: /home/andrei/python/pytest/qa collected 1 item test_psum.py . [100%] ================================================== 1 passed in 0.01s ===================================================

Если функция вернёт результат отличный от ожидаемого, например, в случае ошибки в самом тесте

assert add(2, 3) == 999, "Should be 999"

pytest test_psum.py

================================================= test session starts ================================================== platform linux -- Python 3.9.5, pytest-7.0.1, pluggy-1.0.0 rootdir: /home/andrei/python/pytest/qa collected 1 item test_psum.py F [100%] ======================================================= FAILURES ======================================================= ______________________________________________________ test_psum _______________________________________________________ def test_psum(): > assert add(2, 3) == 999, "Should be 999" E AssertionError: Should be 999 E assert 5 == 999 E + where 5 = add(2, 3) test_psum.py:4: AssertionError =============================================== short test summary info ================================================ FAILED test_psum.py::test_psum - AssertionError: Should be 999 ================================================== 1 failed in 0.02s ===================================================

Структура проекта с тестами на PyTest

Как только проект перерастает микроскопический размер держать тесты в одной директории с кодом становится неудобно

Предположим, что код проекта лежит в директории app можно переложить файл с тестом test_psum.py в поддиректорию tests

app ├── psum.py ├── __pycache__ │ ├── psum.cpython-39.pyc │ └── test_psum.cpython-39-pytest-7.0.1.pyc ├── tests │ ├── __pycache__ │ └── test_psum.py └── venv ├── bin ├── include ├── lib ├── lib64 -> lib └── pyvenv.cfg

Если теперь запустить тест из директории app то pytest поймёт как импортировать psum

python -m pytest tests/test_psum.py

Если запустить этот же тест прямо из директории tests - появится ошибка ImportError while importing test module

Тестирование проверки аргументов на тип

Демонстрацю применения PyTest часто начинают с функций сложения или умножения.

Например

# Code def prod(a, b): return a * b

# Test from prod import prod def test_prod(): res = prod(3, 4) assert res == 12

python -m pytest tests/test_prod.py

Эта функция подразумевает использование чисел. Но валидации аргументов нет.

С помощью добавим валидацию и напишем тест.

# prod.py def prod(a, b): if not all( map(lambda p: isinstance(p, (int, float)), (a, b)) ): raise TypeError("Not valid argument data type") print("prod.py: Valid arguments") return a * b

# test_prod.py import pytest from prod import prod def test_prod(): res = prod(3, 4) assert res == 12 def test_arguments(): try: # Заведомо неправильный тип данных должен быть пойман # "" is a string and should not be accepted res = prod("", 4) except TypeError as err: # if TypeError is caught pass else: print("test_prod.py: Invalid argument is not caught") pytest.fail() # assert False

Вместо pytest.fail() можно использовать assert False, тогде и импортировать pytest необязательно

python -m pytest -v tests/test_prod.py

================================ test session starts ================================ platform linux -- Python 3.9.5, pytest-7.0.1, pluggy-1.0.0 -- /home/andrei/python/pytest/otus/venv/bin/python cachedir: .pytest_cache rootdir: /home/andrei/python/pytest/app collected 2 items tests/test_prod.py::test_prod PASSED [ 50%] tests/test_prod.py::test_arguments PASSED [100%] ================================= 2 passed in 0.01s =================================

Если тест сломается PyTest выдаст следующий результат

================================ test session starts ================================ platform linux -- Python 3.9.5, pytest-7.0.1, pluggy-1.0.0 -- /home/andrei/python/pytest/otus/venv/bin/python cachedir: .pytest_cache rootdir: /home/andrei/python/pytest/app collected 2 items tests/test_prod.py::test_prod PASSED [ 50%] tests/test_prod.py::test_arguments FAILED [100%] ===================================== FAILURES ====================================== __________________________________ test_arguments ___________________________________ def test_arguments(): try: # "" is a string and should not be accepted res = prod(3, 4) except TypeError as err: # if TypeError is caught pass else: print("test_prod.py: Invalid argument is not caught") > pytest.fail() E Failed tests/test_prod.py:19: Failed ------------------------------- Captured stdout call -------------------------------- prod.py: Valid arguments test_prod.py: Invalid argument is not caught ============================== short test summary info ============================== FAILED tests/test_prod.py::test_arguments - Failed ============================ 1 failed, 1 passed in 0.02s ============================

unittest из PyTest

Если на проекте уже есть тесты, созданные на unittest можно их не переписывать - PyTest поймёт синтаксис unittest

Рассмотрим проверку решения квадратного уравнения на unittest

Если запустить эти же тесты с помощью PyTest

python -m pytest -v tests/test_quadratic.py

================================================== test session starts =================================================== platform linux -- Python 3.9.5, pytest-7.0.1, pluggy-1.0.0 -- /home/andrei/python/unittest/app/venv/bin/python cachedir: .pytest_cache rootdir: /home/andrei/python/unittest/app collected 5 items tests/test_quadratic.py::TestQuadratic::test_raises_type_error PASSED [ 20%] tests/test_quadratic.py::TestQuadratic::test_result_is_tuple PASSED [ 40%] tests/test_quadratic.py::TestQuadratic::test_single_root PASSED [ 60%] tests/test_quadratic.py::TestQuadratic::test_two_roots PASSED [ 80%] tests/test_quadratic.py::TestQuadratic::test_zero_a_and_b PASSED [100%] =================================================== 5 passed in 0.01s ====================================================

Добавить PyTest в Pycharm

Чтобы добавить PyTest в PyCharm воспользуйтесь следующей инструкцией

Settings (Ctrl + Alt + S) → Tools → Python Integrated Tools → Default test runner:

Выбрать pytest. Если он ещё не был добавлен появится предупреждение и кнопка Fix.

Добавить PyTest в PyCharm изображение с сайта www.aredel.com
PyTest не найден

Нужно нажать кнопку Fix

Внизу главного экрана настроек появится сообщение

Installing package 'pytest'

Дождитесь когда оно сменится сообщением об успешной установке и с экрана исчезнет предупреждение.

Добавить PyTest в PyCharm изображение с сайта www.aredel.com
PyTest как default test runner
Related Articles
Тестирование
Python