Интеграция с FastAPI и Django, тестирование REST и GraphQL endpoints
«Автоматическое нахождение багов в API до того, как их найдут пользователи.»
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from hypothesis import given, strategies as st
from starlette.testclient import TestClient
app = FastAPI()
class Item(BaseModel):
name: str
price: float
quantity: int = 1
@app.post("/items/")
async def create_item(item: Item):
if item.price < 0:
raise HTTPException(status_code=400, detail="Price must be positive")
if item.quantity < 1:
raise HTTPException(status_code=400, detail="Quantity must be at least 1")
return {"id": 1, **item.dict()}
client = TestClient(app)@st.composite
def valid_item_strategy(draw):
return {
"name": draw(st.text(min_size=1, max_size=100)),
"price": draw(st.floats(min_value=0.01, max_value=10000, allow_nan=False)),
"quantity": draw(st.integers(min_value=1, max_value=1000))
}
@given(valid_item_strategy())
def test_create_item_success(item_data):
response = client.post("/items/", json=item_data)
assert response.status_code == 200
data = response.json()
assert data["name"] == item_data["name"]
assert data["price"] == item_data["price"]
@st.composite
def invalid_item_strategy(draw):
"""Генерирует невалидные данные для проверки валидации"""
return {
"name": draw(st.text(min_size=1, max_size=100)),
"price": draw(st.floats(max_value=-0.01) | st.just(float('nan'))),
"quantity": draw(st.integers(max_value=0))
}
@given(invalid_item_strategy())
def test_create_item_validation(item_data):
response = client.post("/items/", json=item_data)
assert response.status_code == 400@given(
st.text(max_size=10000), # Очень длинные строки
st.floats(allow_nan=True, allow_infinity=True),
st.integers(min_value=-1000, max_value=1000)
)
def test_create_item_edge_cases(name, price, quantity):
item_data = {"name": name, "price": price, "quantity": quantity}
response = client.post("/items/", json=item_data)
# API должен корректно обрабатывать любые данные
assert response.status_code in [200, 400, 422]from rest_framework import serializers, viewsets
from django.test import Client
from hypothesis import given, strategies as st
class ProductSerializer(serializers.Serializer):
name = serializers.CharField(max_length=100)
price = serializers.DecimalField(max_digits=10, decimal_places=2)
stock = serializers.IntegerField(min_value=0)
class ProductViewSet(viewsets.ViewSet):
def create(self, request):
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(serializer.validated_data)
# Тесты
client = Client()
@st.composite
def product_strategy(draw):
return {
"name": draw(st.text(min_size=1, max_size=100)),
"price": draw(st.decimals(min_value=0.01, max_value=999999.99, places=2)),
"stock": draw(st.integers(min_value=0, max_value=10000))
}
@given(product_strategy())
def test_product_create(product_data):
response = client.post(
'/api/products/',
data=product_data,
content_type='application/json'
)
assert response.status_code == 200from hypothesis import given, strategies as st
import requests
GRAPHQL_ENDPOINT = "http://localhost:8000/graphql"
@st.composite
def user_input_strategy(draw):
return {
"name": draw(st.text(min_size=1, max_size=50)),
"email": draw(st.emails()),
"age": draw(st.integers(min_value=0, max_value=150))
}
@given(user_input_strategy())
def test_create_user_graphql(user_data):
query = """
mutation CreateUser($input: UserInput!) {
createUser(input: $input) {
id
name
email
}
}
"""
response = requests.post(
GRAPHQL_ENDPOINT,
json={"query": query, "variables": {"input": user_data}}
)
assert response.status_code == 200
data = response.json()
assert "errors" not in data or data["errors"] is NoneHypothesis автоматически находит баги в API, генерируя неожиданные входные данные и проверяя ответы на корректность.
Следующая тема: Тестирование баз данных — SQLAlchemy, транзакции, миграции.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.