Тестирование федерации схем
Updated Jul 2026
Вызов федерации
При использовании Apollo Federation или склеивания схем несколько сервисов подграфов вносят типы и поля в составной суперграф. Тестирование должно верифицировать корректность композиции, разрешение ссылок на сущности через границы подграфов и отсутствие конфликтующих определений типов.
Архитектура федерации
+------------------+ +------------------+ +------------------+
| ProductService | | OrderService | | UserService |
| Subgraph | | Subgraph | | Subgraph |
| | | | | |
| type Product | | type Order | | type User |
| @key(id) | | items: [Item] | | @key(id) |
| name: String | | customer: User | | name: String |
| price: Float | | | | email: String |
+--------+---------+ +--------+---------+ +--------+---------+
| | |
+---------------------+---------------------+
|
+----------+----------+
| Apollo Gateway |
| (Composed Schema) |
+---------------------+
Тест 1: Все типы подграфов разрешимы
class TestSchemaFederation:
"""Test that federated GraphQL schemas compose correctly."""
def test_all_subgraph_types_resolvable(self, composed_schema, subgraphs):
"""Every type defined in a subgraph should be resolvable
in the composed schema."""
for subgraph_name, subgraph_schema in subgraphs.items():
for type_name in subgraph_schema.get_types():
if type_name.startswith("__"): # Skip introspection types
continue
assert type_name in composed_schema.get_types(), (
f"Type '{type_name}' from subgraph '{subgraph_name}' "
f"is not resolvable in the composed schema"
)
Тест 2: Ссылки на сущности разрешаются между подграфами
def test_entity_references_resolve(self, gateway_client):
"""Entity references across subgraphs should resolve without errors."""
# Product is defined in ProductService, but Order references it
query = """
query {
order(id: "order-123") {
id
items {
product {
id
name # Resolved from ProductService
price # Resolved from ProductService
}
quantity
}
}
}
"""
response = gateway_client.execute(query)
assert "errors" not in response, (
f"Entity resolution failed: {response.get('errors')}"
)
assert response["data"]["order"]["items"][0]["product"]["name"] is not None
Тест 3: Нет конфликтующих определений типов
Когда два подграфа расширяют один тип, определения их полей должны быть совместимы:
def test_no_conflicting_type_definitions(self, subgraphs):
"""Subgraphs should not define conflicting field types for shared types."""
type_fields = {}
conflicts = []
for subgraph_name, schema in subgraphs.items():
for type_name, type_def in schema.get_types().items():
if type_name.startswith("__"):
continue
if type_name not in type_fields:
type_fields[type_name] = {}
for field_name, field_type in type_def.fields.items():
if field_name in type_fields[type_name]:
existing = type_fields[type_name][field_name]
if field_type != existing["type"]:
conflicts.append(
f"Field '{type_name}.{field_name}' has conflicting types: "
f"'{existing['type']}' in {existing['subgraph']} vs "
f"'{field_type}' in {subgraph_name}"
)
type_fields[type_name][field_name] = {
"type": field_type,
"subgraph": subgraph_name
}
assert not conflicts, (
f"Found {len(conflicts)} type conflicts:\n" + "\n".join(conflicts)
)
Тест 4: Здоровье и доступность подграфов
def test_all_subgraphs_healthy(self, subgraph_endpoints):
"""Each subgraph should respond to health checks."""
for name, url in subgraph_endpoints.items():
response = httpx.get(f"{url}/.well-known/apollo/server-health")
assert response.status_code == 200, (
f"Subgraph '{name}' at {url} is unhealthy: {response.status_code}"
)
def test_gateway_introspection(self, gateway_client):
"""Gateway should respond to introspection queries."""
query = """
{
__schema {
types { name }
queryType { name }
}
}
"""
response = gateway_client.execute(query)
assert "errors" not in response
type_names = [t["name"] for t in response["data"]["__schema"]["types"]]
# Verify key types from each subgraph are present
assert "Product" in type_names
assert "Order" in type_names
assert "User" in type_names
Тест 5: Производительность кросс-подграфных запросов
def test_cross_subgraph_query_latency(self, gateway_client):
"""Queries spanning multiple subgraphs should meet latency SLA."""
# This query touches all three subgraphs
query = """
query {
user(id: "user-1") {
name # UserService
orders(first: 5) { # OrderService
id
total
items {
product {
name # ProductService
price
}
}
}
}
}
"""
import time
start = time.monotonic()
response = gateway_client.execute(query)
duration = time.monotonic() - start
assert "errors" not in response
assert duration < 3.0, (
f"Cross-subgraph query took {duration:.2f}s, SLA is 3.0s. "
f"Check query plan and subgraph latencies."
)
Тесты федерации, сгенерированные ИИ
Попросите ИИ сгенерировать тесты федерации из ваших схем подграфов:
Given the following Apollo Federation subgraph schemas, generate tests
that verify correct composition:
Subgraph 1 (ProductService):
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
category: Category!
}
Subgraph 2 (InventoryService):
extend type Product @key(fields: "id") {
id: ID! @external
inStock: Boolean!
warehouseLocation: String
}
Subgraph 3 (ReviewService):
extend type Product @key(fields: "id") {
id: ID! @external
reviews: [Review!]!
averageRating: Float
}
Generate tests for:
1. Each subgraph's fields resolve correctly through the gateway
2. Cross-subgraph queries work (e.g., product with stock AND reviews)
3. Entity reference resolution (no null references)
4. Performance: cross-subgraph queries under 2s
5. Error handling: what happens when one subgraph is down?
Тестирование graceful degradation
def test_gateway_handles_subgraph_failure(self, gateway_client, subgraph_controller):
"""Gateway should return partial data when one subgraph is down."""
# Stop the ReviewService subgraph
subgraph_controller.stop("ReviewService")
query = """
query {
product(id: "prod-1") {
name # From ProductService (available)
price # From ProductService (available)
reviews { # From ReviewService (unavailable)
rating
}
}
}
"""
response = gateway_client.execute(query)
# Product data should still be available
assert response["data"]["product"]["name"] is not None
assert response["data"]["product"]["price"] is not None
# Reviews should be null or have an error
assert (
response["data"]["product"]["reviews"] is None
or "errors" in response
)
# Restart for other tests
subgraph_controller.start("ReviewService")
Ключевой вывод
Тестирование федерации верифицирует, что независимо разработанные подграфы корректно компонуются в работающий суперграф. Критические тесты: разрешение типов через границы подграфов, обнаружение конфликтов между общими типами, производительность кросс-подграфных запросов и graceful degradation при сбое подграфа. ИИ может генерировать эти тесты из SDL-схем подграфов, систематически покрывая корректность композиции и разрешение ссылок на сущности.