Spec-Zone.ru › pytest

Как использовать тесты на основе unittest с pytest

pytest поддерживает запуск тестов на основе Python unittest из коробки. Это позволяет использовать существующие наборы тестов на основе unittest с pytest в качестве средства запуска тестов, а также постепенно адаптировать набор тестов, чтобы в полной мере воспользоваться возможностями pytest.

Чтобы запустить существующий набор тестов в стиле unittest с помощью pytest, введите:

pytest tests

pytest автоматически соберёт подклассы unittest.TestCase и их методы test в файлах test_*.py или *_test.py.

Поддерживаются почти все возможности unittest:

  • unittest.skip()/unittest.skipIf() декораторы
  • unittest.TestCase.setUp()/unittest.TestCase.tearDown()
  • unittest.TestCase.setUpClass()/unittest.TestCase.tearDownClass()
  • unittest.setUpModule()/unittest.tearDownModule()
  • unittest.TestCase.subTest() (начиная с версии 9.0)

На данный момент pytest не поддерживает следующие возможности:

  • протокол load_tests;

Преимущества из коробки

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

  • Получить более информативные трассировки стека;
  • перехват stdout и stderr;
  • параметры выбора тестов с помощью флагов -k и -m;
  • остановка после первой (или N) ошибки;
  • –pdb — параметр командной строки для отладки при сбоях тестов (см. примечание ниже);
  • Распределение тестов по нескольким процессорам с помощью плагина pytest-xdist;
  • Использование обычных инструкций assert вместо функций self.assert* (в этом очень помогает unittest2pytest);

Возможности pytest в подклассах unittest.TestCase

В подклассах unittest.TestCase работают следующие возможности pytest:

  • Метки: skip, skipif, xfail;
  • Автоматически используемые фикстуры;

Следующие возможности pytest не работают и, вероятно, никогда не будут работать из-за различий в философии проектирования:

  • Фикстуры (за исключением фикстур autouse, см. ниже);
  • Параметризация;
  • Пользовательские хуки;

Сторонние плагины могут работать или не работать должным образом — это зависит от плагина и набора тестов.

Добавление фикстур pytest в подклассы unittest.TestCase с помощью меток

Запуск тестов unittest с помощью pytest позволяет использовать механизм фикстур pytest в тестах в стиле unittest.TestCase. Предположим, вы хотя бы бегло ознакомились с возможностями фикстур pytest. Рассмотрим пример, в котором используется фикстура pytest db_class для создания кэшируемого на уровне класса объекта базы данных, на который затем ссылается тест в стиле unittest:

# content of conftest.py

# we define a fixture function below and it will be "used" by
# referencing its name from tests

import pytest


@pytest.fixture(scope="class")
def db_class(request):
    class DummyDB:
        pass

    # set a class attribute on the invoking test context
    request.cls.db = DummyDB()

Здесь определяется функция фикстуры db_class, которая при использовании вызывается один раз для каждого класса тестов и устанавливает атрибут db на уровне класса, присваивая ему экземпляр DummyDB. Функция фикстуры получает специальный объект request, предоставляющий доступ к контексту запрашивающего теста, например к атрибуту cls, указывающему на класс, в котором используется фикстура. Такая архитектура отделяет написание фикстур от кода самих тестов и позволяет повторно использовать фикстуру, просто сославшись на неё по имени. Теперь напишем класс unittest.TestCase, используя определённую нами фикстуру:

# content of test_unittest_db.py

import unittest

import pytest


@pytest.mark.usefixtures("db_class")
class MyTest(unittest.TestCase):
    def test_method1(self):
        assert hasattr(self, "db")
        assert 0, self.db  # fail for demo purposes

    def test_method2(self):
        assert 0, self.db  # fail for demo purposes

Декоратор класса @pytest.mark.usefixtures("db_class") гарантирует, что функция фикстуры pytest db_class будет вызвана один раз для каждого класса. Благодаря намеренно завершающимся ошибкой инструкциям assert мы можем посмотреть значения self.db в трассировке стека:

$ pytest test_unittest_db.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items

test_unittest_db.py FF                                               [100%]

================================= FAILURES =================================
___________________________ MyTest.test_method1 ____________________________

self = <test_unittest_db.MyTest testMethod=test_method1>

    def test_method1(self):
        assert hasattr(self, "db")
>       assert 0, self.db  # fail for demo purposes
        ^^^^^^^^^^^^^^^^^
E       AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E       assert 0

test_unittest_db.py:11: AssertionError
___________________________ MyTest.test_method2 ____________________________

self = <test_unittest_db.MyTest testMethod=test_method2>

    def test_method2(self):
>       assert 0, self.db  # fail for demo purposes
        ^^^^^^^^^^^^^^^^^
E       AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E       assert 0

test_unittest_db.py:14: AssertionError
========================= short test summary info ==========================
FAILED test_unittest_db.py::MyTest::test_method1 - AssertionError: <conft...
FAILED test_unittest_db.py::MyTest::test_method2 - AssertionError: <conft...
============================ 2 failed in 0.12s =============================

Эта стандартная трассировка стека pytest показывает, что оба метода тестирования используют один и тот же экземпляр self.db, как и предполагалось при написании функции фикстуры с областью видимости класса.

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

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

Вы можете пометить функции фикстур флагом @pytest.fixture(autouse=True) и определить функцию фикстуры в контексте, где она должна использоваться. Рассмотрим фикстуру initdir, которая запускает все методы тестирования класса TestCase во временном каталоге с предварительно инициализированным samplefile.ini. Сама фикстура initdir использует встроенную фикстуру pytest tmp_path, чтобы создать отдельный временный каталог для каждого теста:

# content of test_unittest_cleandir.py
import unittest

import pytest


class MyTest(unittest.TestCase):
    @pytest.fixture(autouse=True)
    def initdir(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)  # change to pytest-provided temporary directory
        tmp_path.joinpath("samplefile.ini").write_text("# testdata", encoding="utf-8")

    def test_method(self):
        with open("samplefile.ini", encoding="utf-8") as f:
            s = f.read()
        assert "testdata" in s

Благодаря флагу autouse функция фикстуры initdir будет использоваться для всех методов класса, в котором она определена. Это сокращённый вариант применения маркера @pytest.mark.usefixtures("initdir") к классу, как в предыдущем примере.

Запуск этого модуля тестов …:

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

… даёт один пройденный тест, поскольку функция фикстуры initdir была выполнена до test_method.

Примечание

Методы unittest.TestCase не могут напрямую получать аргументы фикстур, поскольку такая возможность, вероятно, помешала бы запуску обычных наборов тестов unittest.TestCase.

Примеры выше с usefixtures и autouse помогут добавить фикстуры pytest в наборы тестов unittest.

Вы также можете постепенно отказаться от наследования от unittest.TestCase, перейти к обычным инструкциям assert и затем поэтапно начать пользоваться полным набором возможностей pytest.

Примечание

Из-за архитектурных различий между двумя фреймворками настройка и очистка для тестов на основе unittest выполняются на этапе call тестирования, а не на стандартных этапах pytest и setup фреймворка teardown. В некоторых ситуациях это важно понимать, особенно при анализе ошибок. Например, если при настройке набора тестов на основе unittest возникают ошибки, pytest не сообщит об ошибках на этапе setup, а вместо этого выдаст ошибку на этапе call.

© 2015–2026 Holger Krekel and pytest-dev team
Licensed under the MIT License.
https://docs.pytest.org/en/stable/how-to/unittest.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API