Перейти к содержимому
К программе курса
Pytest с нуля: Первые тесты за 2.5 часа
2 / 825%

Базовая структура: где хранить тесты

15 минут

В первом уроке вы запустили тест прямо в корне проекта. Но в реальных проектах тесты хранятся отдельно от кода.

Цель: Научиться правильно организовывать тесты, чтобы проект был понятен вам и вашей команде.

Вы точно готовы?

Убедитесь, что прошли урок 0:

# Вы должны уметь запустить этот тест
def test_simple():
    assert 2 + 2 == 4

Если не проходили — вернитесь к Первому тесту за 10 минут.

Проблема: тесты в корне проекта

Плохая структура:

my-project/
├── test_user.py
├── test_auth.py
├── test_api.py
├── user.py
├── auth.py
└── api.py

Проблемы:

  • ❌ Тесты смешаны с кодом
  • ❌ Сложно найти нужный тест
  • ❌ При деплое тесты попадут в production

Правильная структура: tests/ директория

Создайте структуру

cd pytest-tutorial
 
# Создайте директории
mkdir src
mkdir tests
 
# Структура должна быть такой:
# pytest-tutorial/
# ├── src/          # Код приложения
# ├── tests/        # Тесты
# └── .venv/        # Виртуальное окружение

Создайте код приложения

# Создайте файл src/calculator.py
touch src/calculator.py

Напишите простой код:

# src/calculator.py
 
def add(a, b):
    """Складывает два числа"""
    return a + b
 
def subtract(a, b):
    """Вычитает второе число из первого"""
    return a - b
 
def multiply(a, b):
    """Умножает два числа"""
    return a * b

Создайте тест

# Создайте файл tests/test_calculator.py
touch tests/test_calculator.py

Напишите тест:

# tests/test_calculator.py
 
from src.calculator import add, subtract, multiply
 
def test_add():
    """Тест сложения"""
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
 
def test_subtract():
    """Тест вычитания"""
    assert subtract(5, 3) == 2
    assert subtract(0, 5) == -5
 
def test_multiply():
    """Тест умножения"""
    assert multiply(3, 4) == 12
    assert multiply(-2, 3) == -6

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

Из корня проекта

# Убедитесь что вы в pytest-tutorial/
pwd
 
# Запустите все тесты
pytest tests/ -v

Ожидаемый результат:

============================ test session starts ============================
collected 3 items
 
tests/test_calculator.py::test_add PASSED                            [ 33%]
tests/test_calculator.py::test_subtract PASSED                       [ 66%]
tests/test_calculator.py::test_multiply PASSED                       [100%]
 
============================= 3 passed in 0.02s =============================

✅ Pytest автоматически нашёл все тесты в tests/ директории!

Запуск одного файла

# Запустить только test_calculator.py
pytest tests/test_calculator.py -v

Запуск одного теста

# Запустить только test_add
pytest tests/test_calculator.py::test_add -v

Правила именования pytest

Pytest автоматически находит тесты по правилам именования:

Правило #1: Файлы

# ✅ ХОРОШО — pytest найдёт
test_*.py         # test_calculator.py, test_user.py
*_test.py         # calculator_test.py, user_test.py
 
# ❌ ПЛОХО — pytest НЕ найдёт
calculator.py     # Нет префикса test_
my_tests.py       # Нет префикса test_

Рекомендация: Используйте test_*.py (стандарт в Python сообществе).

Правило #2: Функции

# ✅ ХОРОШО — pytest найдёт
def test_addition():
    pass
 
def test_user_creation():
    pass
 
# ❌ ПЛОХО — pytest НЕ найдёт
def addition_test():    # Нет префикса test_
    pass
 
def validate_user():    # Нет префикса test_
    pass

Правило #3: Классы (опционально)

# ✅ ХОРОШО — pytest найдёт
class TestCalculator:
    def test_add(self):
        pass
 
# ❌ ПЛОХО — pytest НЕ найдёт
class Calculator:       # Нет префикса Test
    def test_add(self):
        pass

Примечание: Классы для тестов мы разберём в уроке 2.

Pytest Discovery: как pytest находит тесты

Когда вы запускаете pytest, он:

Шаг 1: Ищет директории

pytest-tutorial/
├── tests/          ← Проверяет эту директорию
├── src/            ← Игнорирует (нет тестов)
└── .venv/          ← Игнорирует

Шаг 2: Ищет файлы

tests/
├── test_calculator.py    ← Находит (начинается с test_)
├── test_user.py          ← Находит
└── helpers.py            ← Игнорирует (нет префикса test_)

Шаг 3: Ищет функции

# tests/test_calculator.py
 
def test_add():              ← Находит
    pass
 
def test_subtract():         ← Находит
    pass
 
def helper_function():       ← Игнорирует (нет префикса test_)
    pass

Проверка: что pytest нашёл

# Покажет список всех найденных тестов БЕЗ запуска
pytest --collect-only

Результат:

<Module tests/test_calculator.py>
  <Function test_add>
  <Function test_subtract>
  <Function test_multiply>

Организация больших проектов (бонус)

Для сложных проектов:

pytest-tutorial/
├── src/
│   ├── user/
│   │   ├── __init__.py
│   │   ├── models.py
│   │   └── services.py
│   └── auth/
│       ├── __init__.py
│       └── handlers.py
├── tests/
│   ├── user/
│   │   ├── test_models.py
│   │   └── test_services.py
│   └── auth/
│       └── test_handlers.py
└── pytest.ini

Зеркальная структура: tests/ повторяет структуру src/ для удобства навигации.

Типичные ошибки

Ошибка #1: ModuleNotFoundError

# tests/test_calculator.py
from calculator import add  # ❌ ПЛОХО

Ошибка:

ModuleNotFoundError: No module named 'calculator'

Исправление:

from src.calculator import add  # ✅ ХОРОШО

Ошибка #2: Тесты не в tests/

my-project/
├── src/
└── my_tests/      ← ❌ pytest не найдёт (неправильное имя)
    └── test_user.py

Исправление: Переименуйте my_tests/ в tests/.

Ошибка #3: Забыли __init__.py (Python < 3.3)

Для старых проектов может потребоваться:

touch src/__init__.py
touch tests/__init__.py

Но для современных проектов (Python 3.3+) это не обязательно!

Что вы изучили

  • Создали tests/ директорию для организации файлов
  • Импортировали код из src/ в тесты
  • Поняли правила именования test_*.py и def test_*()
  • Узнали про pytest discovery — автоматический поиск тестов
  • Запускали тесты разными способами (все, один файл, один тест)

Следующий урок

Отлично! Теперь вы знаете где писать тесты. Но как тестировать классы? Как проверять исключения (ошибки)?

Переходите к уроку 2: Тестируем классы и ошибки

В следующем уроке вы узнаете:

  • Как тестировать методы классов
  • Как проверять что код выбрасывает исключения (pytest.raises)
  • Как писать тесты для edge cases

Устранение неисправностей

Запускайте pytest из корня проекта и используйте правильный импорт:from src.calculator import add

Проверьте:

  • Вы в корне проекта (`pytest-tutorial/`)
  • Импорт пишется как `from src.calculator import add`

Проверьте именование файлов и функций, чтобы pytest их обнаружил

Проверьте:

  • Файл: `test_*.py` или `*_test.py`
  • Функция: `def test_*()`
  • Проверьте сбор тестов: `pytest --collect-only`

Убедитесь, что запускаете pytest из правильной директории

Проверьте:

  • Проверьте `pwd`: вы должны быть в корне проекта
  • Файлы названы по паттерну `test_*.py` или `*_test.py`

Для старых версий Python добавьте пустой __init__.py в директорию src/

Базовая структура: где хранить тесты — Pytest с нуля: Первые тесты за 2.5 часа — Potapov.me