Приступить к работе

Установить pytest

pytest Требуется: Python 3.7+ или PyPy3.

  1. Выполните следующую команду в командной строке:

pip install -U pytest
  1. Убедитесь, что вы установили правильную версию:

$ pytest --version
pytest 7.2.0

Создайте свой первый тест

Создайте новый файл с именем test_sample.py, содержащий функцию и тест:

# content of test_sample.py
def func(x):
    return x + 1


def test_answer():
    assert func(3) == 5

Тест

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item

test_sample.py F                                                     [100%]

================================= FAILURES =================================
_______________________________ test_answer ________________________________

    def test_answer():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_sample.py:6: AssertionError
========================= short test summary info ==========================
FAILED test_sample.py::test_answer - assert 4 == 5
============================ 1 failed in 0.12s =============================

[100%] относится к общему ходу выполнения всех тестовых примеров. После завершения pytest выдает отчет о неудаче, поскольку func(3) не возвращает 5.

Примечание

Вы можете использовать оператор assert для проверки ожиданий теста. Advanced assertion introspection в pytest разумно сообщает промежуточные значения выражения assert, так что вы можете избежать множества имен of JUnit legacy methods.

Проведите несколько тестов

pytest запустит все файлы вида test_*.py или *_test.py в текущем каталоге и его подкаталогах. В более общем случае, он следует за standard test discovery rules.

Утверждать, что определенное исключение вызвано

Используйте помощник raises для утверждения, что некоторый код вызывает исключение:

# content of test_sysexit.py
import pytest


def f():
    raise SystemExit(1)


def test_mytest():
    with pytest.raises(SystemExit):
        f()

Выполните функцию тестирования в режиме «тихого» отчета:

$ pytest -q test_sysexit.py
.                                                                    [100%]
1 passed in 0.12s

Примечание

Флаг -q/--quiet в этом и следующих примерах сохраняет краткость вывода.

Группируйте несколько тестов в классе

После разработки нескольких тестов вы можете захотеть сгруппировать их в класс. pytest позволяет легко создать класс, содержащий более одного теста:

# content of test_class.py
class TestClass:
    def test_one(self):
        x = "this"
        assert "h" in x

    def test_two(self):
        x = "hello"
        assert hasattr(x, "check")

pytest обнаруживает все тесты, следующие за его Conventions for Python test discovery, поэтому он находит обе функции с префиксом test_. Нет необходимости создавать подклассы, но убедитесь, что ваш класс имеет префикс Test, иначе он будет пропущен. Мы можем просто запустить модуль, передав ему имя файла:

$ pytest -q test_class.py
.F                                                                   [100%]
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________

self = <test_class.TestClass object at 0xdeadbeef0001>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr('hello', 'check')

test_class.py:8: AssertionError
========================= short test summary info ==========================
FAILED test_class.py::TestClass::test_two - AssertionError: assert False
1 failed, 1 passed in 0.12s

Первый тест прошел, а второй - нет. Вы можете легко увидеть промежуточные значения в утверждении, которые помогут вам понять причину неудачи.

Группировка тестов по классам может быть полезной по следующим причинам:

  • Организация испытаний

  • Совместное использование приспособлений для тестов только в этом конкретном классе

  • Применение оценок на уровне класса и их неявное применение ко всем тестам

При группировке тестов внутри классов следует помнить о том, что каждый тест имеет уникальный экземпляр класса. Если каждый тест будет использовать один и тот же экземпляр класса, это будет очень вредно для изоляции тестов и будет способствовать плохой практике тестирования. Это описано ниже:

# content of test_class_demo.py
class TestClassDemoInstance:
    value = 0

    def test_one(self):
        self.value = 1
        assert self.value == 1

    def test_two(self):
        assert self.value == 1
$ pytest -k TestClassDemoInstance -q
.F                                                                   [100%]
================================= FAILURES =================================
______________________ TestClassDemoInstance.test_two ______________________

self = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>

    def test_two(self):
>       assert self.value == 1
E       assert 0 == 1
E        +  where 0 = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>.value

test_class_demo.py:9: AssertionError
========================= short test summary info ==========================
FAILED test_class_demo.py::TestClassDemoInstance::test_two - assert 0 == 1
1 failed, 1 passed in 0.12s

Обратите внимание, что атрибуты, добавленные на уровне класса, являются атрибутами класса, поэтому они будут общими для всех тестов.

Запрос уникального временного каталога для функциональных тестов

pytest предоставляет Builtin fixtures/function arguments для запроса произвольных ресурсов, например, уникального временного каталога:

# content of test_tmp_path.py
def test_needsfiles(tmp_path):
    print(tmp_path)
    assert 0

Перечислите имя tmp_path в сигнатуре тестовой функции, и pytest будет искать и вызывать фабрику приспособлений для создания ресурса перед выполнением вызова тестовой функции. Перед запуском теста pytest создает уникальный для каждого теста временный каталог:

$ pytest -q test_tmp_path.py
F                                                                    [100%]
================================= FAILURES =================================
_____________________________ test_needsfiles ______________________________

tmp_path = PosixPath('PYTEST_TMPDIR/test_needsfiles0')

    def test_needsfiles(tmp_path):
        print(tmp_path)
>       assert 0
E       assert 0

test_tmp_path.py:3: AssertionError
--------------------------- Captured stdout call ---------------------------
PYTEST_TMPDIR/test_needsfiles0
========================= short test summary info ==========================
FAILED test_tmp_path.py::test_needsfiles - assert 0
1 failed in 0.12s

Более подробную информацию о работе с временными каталогами можно найти на Temporary directories and files.

Выясните, какие встроенные pytest fixtures существуют для данной команды:

pytest --fixtures   # shows builtin and custom fixtures

Обратите внимание, что эта команда опускает фикстуры с ведущими _, если не добавлена опция -v.

Продолжить чтение

Ознакомьтесь с дополнительными ресурсами pytest, которые помогут вам настроить тесты для вашего уникального рабочего процесса:

Вернуться на верх