Справочник Python

Полное руководство по языку на русском

Тестирование

pytest — современный тестовый фреймворк

Установка: pip install pytest

# test_calc.py
def add(a, b):
    return a + b  # возврат значения

# Имя теста начинается с test_
def test_add():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0
# Запуск
pytest
pytest test_calc.py
pytest test_calc.py::test_add
pytest -v
pytest -k "add"
pytest -x
pytest --tb=short

Фикстуры (fixtures)

import pytest  # импорт тестового фреймворка

@pytest.fixture  # фикстура pytest
def user_data():
    return {"name": "Анна", "age": 30}  # возврат значения

@pytest.fixture  # фикстура pytest
def database():
    # Setup
    db = create_test_database()  # присваивание
    yield db  # генерация значения
    # Teardown
    db.drop()

def test_user_name(user_data):
    assert user_data["name"] == "Анна"

def test_user_age(user_data):
    assert user_data["age"] == 30

Параметризация

import pytest  # импорт тестового фреймворка

@pytest.mark.parametrize("a, b, expected", [  # параметризация теста
    (1, 2, 3),
    (0, 0, 0),
    (-1, 1, 0),
    (100, -50, 50),
])
def test_add(a, b, expected):
    assert a + b == expected

# Несколько параметров
@pytest.mark.parametrize("a", [1, 2])  # параметризация теста
@pytest.mark.parametrize("b", [3, 4])  # параметризация теста
def test_product(a, b):
    pass  # 4 комбинации

Моки (unittest.mock)

from unittest.mock import Mock, patch  # импорт модуля
import requests  # импорт модуля регулярных выражений

# Mock объект
mock_response = Mock()  # присваивание
mock_response.json.return_value = {"id": 1, "name": "Анна"}
mock_response.status_code = 200

# patch — замена в контексте
@patch("requests.get")  # подмена объекта на mock
def test_fetch(mock_get):
    mock_get.return_value = mock_response
    result = fetch_user(1)  # присваивание
    assert result["name"] == "Анна"

# Проверка вызова
mock_get.assert_called_once_with("https://api.example.com/users/1")  # проверка вызова с аргументами

unittest — встроенный модуль

import unittest  # импорт встроенного тестового модуля

class TestMath(unittest.TestCase):
    def setUp(self):  # подготовка перед каждым тестом
        """Выполняется перед каждым тестом"""
        self.data = [1, 2, 3]  # обращение к атрибуту экземпляра

    def test_sum(self):
        self.assertEqual(sum(self.data), 6)  # обращение к атрибуту экземпляра

    def test_max(self):
        self.assertEqual(max(self.data), 3)  # обращение к атрибуту экземпляра

    def test_in(self):
        self.assertIn(2, self.data)  # обращение к атрибуту экземпляра

if __name__ == "__main__":  # условие
    unittest.main()

Проверки (assertions)

assert result == expected
assert result != wrong
assert result is None
assert result is not None
assert value in collection
assert len(items) > 0  # длина
assert "error" not in response.text
assert response.status_code == 200

# pytest предоставляет
pytest.approx(3.14159, 0.001)  # сравнение float с погрешностью
pytest.raises(ValueError)  # проверка ожидаемого исключения
pytest.warns(UserWarning)  # проверка ожидаемого предупреждения

Проверка исключений

import pytest  # импорт тестового фреймворка

def test_raises():
    with pytest.raises(ValueError, match="деление на ноль"):  # контекстный менеджер
        divide(10, 0)

def test_raises_no_match():
    with pytest.raises(ZeroDivisionError):  # контекстный менеджер
        1 / 0

Покрытие кода (coverage)

# Установка
pip install pytest-cov

# Запуск с покрытием
pytest --cov=my_module --cov-report=html
pytest --cov=my_module --cov-report=term-missing

# Минимальный порог
pytest --cov=my_module --cov-fail-under=80