Тестирование в Python с использованием модулей unittest и pytest: создание и выполнение тестов для проверки работоспособности кода.

онлайн тренажер по питону
Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Самоучитель Python 3, собранный из материалов данного сайта.Предназначен в основном для тех, кто хочет изучить язык программирования Python с нуля.

Unittest - стандартная библиотека Python для тестирования

Unittest - это встроенная в Python библиотека для создания и выполнения модульных тестов. Она предоставляет полный набор инструментов для написания структурированных тестов, организации проверок и управления тестовыми данными.

Основные концепции unittest

TestCase - базовый класс для создания тестов. Все тестовые классы должны наследоваться от unittest.TestCase.

setUp() и tearDown() - специальные методы, которые автоматически выполняются до и после каждого теста. Используются для подготовки тестовых данных и их очистки.

Assertions - методы для проверки условий и результатов выполнения кода. Включают assertEqual(), assertTrue(), assertFalse(), assertRaises() и другие.

Практический пример unittest

import unittest

def add(x, y):
    """Функция сложения двух чисел"""
    return x + y

def divide(x, y):
    """Функция деления с проверкой на ноль"""
    if y == 0:
        raise ValueError("Деление на ноль невозможно")
    return x / y

class TestMathFunctions(unittest.TestCase):
    def setUp(self):
        """Подготовка данных перед каждым тестом"""
        self.test_value = 42
        self.test_list = [1, 2, 3, 4, 5]

    def tearDown(self):
        """Очистка данных после каждого теста"""
        del self.test_value
        del self.test_list

    def test_addition_positive(self):
        """Тест сложения положительных чисел"""
        result = add(5, 3)
        self.assertEqual(result, 8)

    def test_addition_negative(self):
        """Тест сложения отрицательных чисел"""
        result = add(-2, -3)
        self.assertEqual(result, -5)

    def test_setup_value(self):
        """Проверка значения из setUp"""
        self.assertEqual(self.test_value, 42)
        self.assertIsInstance(self.test_value, int)

    def test_list_operations(self):
        """Тест операций со списком"""
        self.assertIn(3, self.test_list)
        self.assertEqual(len(self.test_list), 5)
        self.assertTrue(all(isinstance(x, int) for x in self.test_list))

    def test_division_by_zero(self):
        """Тест исключения при делении на ноль"""
        with self.assertRaises(ValueError):
            divide(10, 0)

    def test_division_normal(self):
        """Тест обычного деления"""
        result = divide(10, 2)
        self.assertEqual(result, 5.0)

if __name__ == '__main__':
    unittest.main()

Основные методы assertions в unittest

  • assertEqual(a, b) - проверяет равенство значений
  • assertNotEqual(a, b) - проверяет неравенство
  • assertTrue(x) - проверяет истинность условия
  • assertFalse(x) - проверяет ложность условия
  • assertIs(a, b) - проверяет идентичность объектов
  • assertIsNone(x) - проверяет на None
  • assertIn(a, b) - проверяет вхождение элемента
  • assertRaises(exc, fun, *args) - проверяет возникновение исключения

Pytest - современный фреймворк для тестирования

Pytest - это мощный и гибкий фреймворк для тестирования Python-приложений. Он отличается простотой синтаксиса, богатой функциональностью и обширной экосистемой плагинов.

Установка pytest

pip install pytest

Основы pytest

# test_math.py
def add(x, y):
    """Функция сложения"""
    return x + y

def multiply(x, y):
    """Функция умножения"""
    return x * y

def test_add_positive():
    """Тест сложения положительных чисел"""
    assert add(2, 3) == 5

def test_add_negative():
    """Тест сложения отрицательных чисел"""
    assert add(-1, -1) == -2

def test_add_zero():
    """Тест сложения с нулем"""
    assert add(5, 0) == 5

def test_multiply():
    """Тест умножения"""
    assert multiply(3, 4) == 12
    assert multiply(-2, 3) == -6

Фикстуры (fixtures) в pytest

Фикстуры позволяют подготавливать данные и ресурсы для тестов, а также управлять их жизненным циклом.

import pytest

@pytest.fixture
def sample_data():
    """Фикстура с тестовыми данными"""
    return {"name": "Python", "version": "3.9", "type": "language"}

@pytest.fixture
def temp_file():
    """Фикстура для создания временного файла"""
    import tempfile
    import os
    
    # Создание временного файла
    fd, path = tempfile.mkstemp()
    yield path
    
    # Очистка после теста
    os.close(fd)
    os.unlink(path)

@pytest.fixture(scope="session")
def database_connection():
    """Фикстура уровня сессии для соединения с БД"""
    # Подключение к базе данных
    connection = "fake_db_connection"
    yield connection
    # Закрытие соединения
    print("Закрытие соединения с БД")

def test_data_access(sample_data):
    """Тест с использованием фикстуры"""
    assert sample_data["name"] == "Python"
    assert sample_data["version"] == "3.9"

def test_file_operations(temp_file):
    """Тест работы с файлом"""
    with open(temp_file, 'w') as f:
        f.write("test content")
    
    with open(temp_file, 'r') as f:
        content = f.read()
        assert content == "test content"

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

import pytest

@pytest.mark.parametrize("a,b,expected", [
    (1, 2, 3),
    (0, 0, 0),
    (-1, 1, 0),
    (10, -5, 5),
])
def test_add_parametrized(a, b, expected):
    """Параметризованный тест сложения"""
    assert add(a, b) == expected

@pytest.mark.parametrize("value,expected", [
    ("hello", True),
    ("", False),
    ("python", True),
    (None, False),
])
def test_string_validation(value, expected):
    """Тест валидации строк"""
    def is_valid_string(s):
        return bool(s and isinstance(s, str))
    
    assert is_valid_string(value) == expected

Маркировка тестов

import pytest

@pytest.mark.slow
def test_slow_operation():
    """Медленный тест"""
    import time
    time.sleep(2)
    assert True

@pytest.mark.fast
def test_fast_operation():
    """Быстрый тест"""
    assert 1 + 1 == 2

@pytest.mark.integration
def test_database_integration():
    """Интеграционный тест"""
    assert True

@pytest.mark.unit
def test_unit_function():
    """Модульный тест"""
    assert len("test") == 4

# Запуск тестов по маркерам:
# pytest -m fast    # только быстрые тесты
# pytest -m slow    # только медленные тесты
# pytest -m "not slow"  # все кроме медленных

Популярные плагины pytest

# pytest-cov - измерение покрытия кода
# Установка: pip install pytest-cov
# Запуск: pytest --cov=my_module tests/

# pytest-html - HTML отчеты
# Установка: pip install pytest-html
# Запуск: pytest --html=report.html

# pytest-xdist - параллельное выполнение
# Установка: pip install pytest-xdist
# Запуск: pytest -n 4  # 4 параллельных процесса

Продвинутые возможности pytest

import pytest

class TestAdvancedFeatures:
    """Класс для демонстрации продвинутых возможностей"""
    
    def test_approximate_comparison(self):
        """Тест приблизительного сравнения"""
        assert 0.1 + 0.2 == pytest.approx(0.3)
        assert [1.0, 2.0, 3.0] == pytest.approx([1.01, 2.01, 3.01], abs=0.1)
    
    def test_exception_handling(self):
        """Тест обработки исключений"""
        with pytest.raises(ValueError, match="invalid literal"):
            int("not_a_number")
    
    def test_warnings(self):
        """Тест предупреждений"""
        import warnings
        with pytest.warns(UserWarning):
            warnings.warn("This is a warning", UserWarning)
    
    @pytest.mark.skip(reason="Функция еще не реализована")
    def test_not_implemented(self):
        """Пропущенный тест"""
        pass
    
    @pytest.mark.skipif(sys.version_info < (3, 8), reason="Требует Python 3.8+")
    def test_python_version_specific(self):
        """Тест для конкретной версии Python"""
        assert True

Сравнение unittest и pytest

Преимущества unittest

  • Встроенность: входит в стандартную библиотеку Python, не требует установки
  • Стабильность: долгая история разработки и широкая поддержка
  • Структурированность: четкая организация тестов в классы и методы
  • Совместимость: работает во всех средах Python без дополнительных зависимостей

Преимущества pytest

  • Простота синтаксиса: минимальный код для написания тестов
  • Мощные фикстуры: гибкая система подготовки данных
  • Богатая экосистема: множество плагинов для расширения функциональности
  • Параметризация: легкое создание множественных тестов
  • Информативные сообщения: подробные отчеты о неудачных тестах
  • Автоматическое обнаружение: находит и запускает тесты без дополнительной настройки

Когда использовать unittest

  • Проекты, где важна минимизация зависимостей
  • Корпоративные среды с ограничениями на внешние библиотеки
  • Команды, предпочитающие объектно-ориентированный подход к тестированию
  • Проекты, требующие сложной иерархии тестов

Когда использовать pytest

  • Новые проекты, где можно выбирать инструменты
  • Команды, ценящие простоту и скорость разработки
  • Проекты, требующие сложной параметризации тестов
  • Случаи, когда нужны специализированные возможности (покрытие кода, параллельное выполнение)

Лучшие практики тестирования

Структура проекта

project/
├── src/
│   └── my_module/
│       └── __init__.py
├── tests/
│   ├── __init__.py
│   ├── test_unit.py
│   ├── test_integration.py
│   └── conftest.py  # для pytest фикстур
├── requirements.txt
└── setup.py

Рекомендации по написанию тестов

  1. Именование: используйте описательные имена для тестов
  2. Изоляция: каждый тест должен быть независимым
  3. Один тест - одна проверка: фокусируйтесь на конкретной функциональности
  4. Подготовка данных: используйте фикстуры для создания тестовых данных
  5. Покрытие кода: стремитесь к высокому покрытию, но не забывайте о качестве тестов

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

 

категории

  • Введение в Python
  • Основы программирования на Python
  • Управляющие конструкции
  • Структуры данных
  • Функции и модули
  • Обработка исключений
  • Работа с файлами и потоками
  • файловая система
  • Объектно-ориентированное программирование (ООП)
  • Регулярные выражения
  • Дополнительные темы
  • Общая база питона