← Назад к вопросам

Как пишешь тесты на Python?

2.0 Middle🔥 231 комментариев
#Python Core#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как пишешь тесты на Python

Тестирование — критическая часть разработки. Расскажу о лучших практиках с использованием pytest.

1. unittest vs pytest

# unittest (встроенный, объектно-ориентированный)
import unittest

class TestMath(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(2 + 2, 4)

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


# pytest (функциональный, более гибкий)
def test_addition():
    assert 2 + 2 == 4

pytest лучше: проще синтаксис, лучше отчёты, встроенные fixtures, параметризованное тестирование.

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

my_project/
├── src/
│   ├── calculator.py
│   └── database.py
├── tests/
│   ├── conftest.py
│   ├── test_calculator.py
│   └── test_database.py
└── pytest.ini

3. Простой пример

# src/calculator.py
def add(a: int, b: int) -> int:
    return a + b

def divide(a: int, b: int) -> float:
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b


# tests/test_calculator.py
import pytest
from src.calculator import add, divide

def test_add():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0

def test_divide_by_zero():
    with pytest.raises(ValueError):
        divide(10, 0)

4. Fixtures — переиспользуемые данные

# conftest.py
import pytest

@pytest.fixture
def sample_user():
    """Пользователь для тестов."""
    return {'id': 1, 'name': 'John', 'email': 'john@example.com'}


# tests/test_users.py
def test_get_user(sample_user):
    assert sample_user['name'] == 'John'
    assert sample_user['email'] == 'john@example.com'

5. Параметризованное тестирование

@pytest.mark.parametrize("input,expected", [
    (2, "even"),
    (3, "odd"),
    (0, "even"),
])
def test_check_even_odd(input, expected):
    result = check_even_odd(input)
    assert result == expected

6. Mock и Patch

from unittest.mock import patch, MagicMock

@patch('src.users.requests.get')
def test_get_user_from_api(mock_get):
    mock_response = MagicMock()
    mock_response.json.return_value = {'id': 1, 'name': 'John'}
    mock_get.return_value = mock_response
    
    result = get_user_from_api(1)
    assert result['name'] == 'John'
    mock_get.assert_called_once()

7. Тестирование исключений

def test_division_error():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide(10, 0)

def test_invalid_input():
    with pytest.raises(TypeError):
        add("not a number", 5)

8. Fixtures с областями

@pytest.fixture(scope="session")
def database_connection():
    """Одна БД для всех тестов."""
    conn = connect_db()
    yield conn
    conn.close()

@pytest.fixture(scope="function")
def fresh_user():
    """Новый пользователь для каждого теста."""
    user = User(name="Test")
    yield user
    user.delete()

9. Тестирование API

from fastapi.testclient import TestClient
from src.api import app

client = TestClient(app)

def test_create_user():
    response = client.post(
        "/users",
        json={"id": 1, "name": "John", "email": "john@example.com"}
    )
    assert response.status_code == 200
    assert response.json()["name"] == "John"

def test_get_nonexistent_user():
    response = client.get("/users/999")
    assert response.status_code == 404

10. Лучшие практики

# Один тест = один случай
def test_login_with_valid_credentials():
    result = login("user", "password")
    assert result.success is True

def test_login_with_invalid_credentials():
    with pytest.raises(AuthError):
        login("user", "wrongpassword")


# Дескриптивные имена
def test_user_cannot_login_with_expired_token():
    pass


# Given-When-Then паттерн
def test_user_deletion():
    # Given
    user = create_user(name="John")
    # When
    delete_user(user.id)
    # Then
    assert not user_exists(user.id)

11. Запуск тестов

pytest                              # Все тесты
pytest tests/test_calculator.py     # Конкретный файл
pytest tests/test_calculator.py::test_add  # Конкретный тест
pytest -v                           # Verbose
pytest -x                           # Стоп при первой ошибке
pytest --cov=src                    # Покрытие кода

12. Test Coverage

pip install pytest-cov
pytest --cov=src --cov-report=html

Тестирование в Python — инвестиция в стабильность. Используй pytest, пишешь тесты для критичных функций, стремись к coverage >= 80%.

Как пишешь тесты на Python? | PrepBro