Каковы различия между фреймворками Pytest и unittest в Python?

Ответ

unittest — это встроенный в Python фреймворк для модульного тестирования, являющийся частью стандартной библиотеки. pytest — популярный сторонний фреймворк, предлагающий более современный и гибкий подход к написанию тестов. Оба используются для написания и запуска тестов, но имеют существенные различия в синтаксисе, функциональности и экосистеме.

1. Синтаксис написания тестов

  • unittest: Требует создания классов, наследующихся от unittest.TestCase, и использования специальных методов assert* (например, assertEqual(), assertTrue()). Это может приводить к более многословному коду.

    import unittest
    
    class TestMath(unittest.TestCase):
        def test_addition(self):
            self.assertEqual(1 + 1, 2)
    
        def test_subtraction(self):
            self.assertTrue(5 - 3 == 2)
  • pytest: Позволяет писать тесты как обычные функции или методы, используя стандартный оператор assert Python. Это делает тесты более читаемыми, лаконичными и интуитивно понятными, так как не требует изучения специфических методов.

    # test_math.py
    def test_addition():
        assert 1 + 1 == 2
    
    def test_subtraction():
        assert 5 - 3 == 2

2. Фикстуры (Fixtures)

  • unittest: Использует методы setUp() и tearDown() (или setUpClass/tearDownClass) для подготовки и очистки тестового окружения. Они привязаны к жизненному циклу класса или метода и могут быть менее гибкими для переиспользования.

    import unittest
    
    class TestDatabase(unittest.TestCase):
        def setUp(self):
            # Подключение к БД перед каждым тестом
            self.db_conn = connect_to_db()
    
        def tearDown(self):
            # Закрытие соединения после каждого теста
            self.db_conn.close()
    
        def test_query(self):
            result = self.db_conn.execute("SELECT 1")
            self.assertEqual(result, 1)
  • pytest: Предлагает более гибкую и мощную систему фикстур с помощью декоратора @pytest.fixture. Фикстуры могут быть функциями, возвращающими данные, и внедряться в тесты по имени. Это позволяет легко переиспользовать логику подготовки/очистки, управлять их областью видимости (функция, модуль, сессия) и создавать сложные зависимости.

    import pytest
    
    @pytest.fixture
    def db_connection():
        # Подготовка ресурса (например, подключение к БД)
        conn = connect_to_db()
        yield conn # Тесты, использующие эту фикстуру, выполняются здесь
        # Очистка ресурса после выполнения тестов
        conn.close()
    
    def test_query(db_connection):
        result = db_connection.execute("SELECT 1")
        assert result == 1

3. Параметризация тестов

  • unittest: Параметризация сложнее, часто требует использования subTest (для Python 3.4+) или внешних библиотек, что может усложнить код.
  • pytest: Встроенная и мощная поддержка параметризации с помощью декоратора @pytest.mark.parametrize, что позволяет легко запускать один и тот же тест с разными наборами входных данных, значительно сокращая дублирование кода.

    import pytest
    
    @pytest.mark.parametrize("a, b, expected", [
        (1, 1, 2),
        (2, 3, 5),
        (0, 0, 0),
    ])
    def test_addition(a, b, expected):
        assert a + b == expected

4. Отчеты и плагины

  • unittest: Генерирует базовые отчеты. Расширение функциональности часто требует написания кастомных тестовых раннеров.
  • pytest: Генерирует более информативные и читаемые отчеты по умолчанию, включая подробную информацию о сбоях. Имеет богатую экосистему плагинов (например, pytest-cov для покрытия кода, pytest-xdist для параллельного выполнения, pytest-mock для моков), что значительно расширяет его возможности и гибкость.

Вывод

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