Короткий ответ
Нужно отдельно проверить успешную валидацию корректных данных, явный отказ при невалидных данных (через pytest.raises(ValidationError)) и граничные значения — минимум/максимум допустимой длины, диапазона, пустые и None значения там, где они формально допустимы или недопустимы.
Как это работает подробнее
Pydantic при создании экземпляра модели с некорректными данными бросает ValidationError со списком конкретных ошибок по каждому полю отдельно; тест может проверить не только факт ошибки, но и то, что она относится к ожидаемому полю и имеет ожидаемый тип, через анализ e.value.errors().
Пример кода
testing_example_317.py
from pydantic import BaseModel, Field, ValidationError
import pytest
class CreateUserRequest(BaseModel):
name: str = Field(min_length=1, max_length=50)
age: int = Field(ge=0, le=120)
@pytest.mark.parametrize("age", [-1, 121])
def test_create_user_request_rejects_invalid_age(age):
with pytest.raises(ValidationError) as exc_info:
CreateUserRequest(name="Ann", age=age)
assert exc_info.value.errors()[0]["loc"] == ("age",)Что использовать на практике
- Практика. Параметризация (@pytest.mark.parametrize) хорошо подходит для перебора набора граничных и некорректных значений одного поля: пустая строка, строка ровно на границе max_length, отрицательное число для поля с ge=0, неверный формат email. Это системно покрывает значимые классы эквивалентности входных данных.
Подводные камни
- Риск. Тестировать только один успешный пример и считать схему полностью проверенной, упуская граничные условия, где чаще всего и возникают реальные баги в production. Проверять сообщение об ошибке дословно вместо факта и места ошибки, из-за чего тест становится хрупким к любым правкам текста сообщения.