Testing¶
This guide covers testing practices for PolyAPI.
Running Tests¶
Gateway Tests¶
cd gateway
pytest tests/ -v
With Coverage¶
cd gateway
pytest tests/ --cov=. --cov-report=html
Run Specific Tests¶
cd gateway
pytest tests/test_api.py::test_sort_strings_asc -v
Test Structure¶
gateway/tests/
├── __init__.py
└── test_api.py
Writing Tests¶
Basic API Test¶
import pytest
from httpx import AsyncClient, ASGITransport
from main import app
@pytest.fixture
def anyio_backend():
return "asyncio"
@pytest.mark.asyncio
async def test_health_check():
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.get("/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "ok"
Module Test¶
@pytest.mark.asyncio
async def test_sort_strings_asc():
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.post(
"/sort",
json={"payload": {"items": ["zebra", "ant", "mango"], "order": "asc"}},
)
assert response.status_code == 200
data = response.json()
assert data["status"] == "success"
assert data["data"]["sorted"] == ["ant", "mango", "zebra"]
Error Test¶
@pytest.mark.asyncio
async def test_sort_empty_array():
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
response = await client.post(
"/sort", json={"payload": {"items": [], "order": "asc"}}
)
assert response.status_code == 200
data = response.json()
assert data["status"] == "error"
assert data["error"]["code"] == "EMPTY_INPUT"
Integration Testing¶
Start Services for Integration Tests¶
docker-compose up -d
Test Against Running Services¶
import httpx
import pytest
@pytest.mark.asyncio
async def test_integration():
async with httpx.AsyncClient() as client:
response = await client.post(
"http://localhost:8000/sort",
json={"payload": {"items": [3, 1, 2], "order": "asc"}}
)
assert response.status_code == 200
assert response.json()["data"]["sorted"] == [1, 2, 3]
Mocking¶
Mock External Services¶
from unittest.mock import patch
@pytest.mark.asyncio
async def test_with_mocked_module():
with patch("httpx.AsyncClient") as mock_client:
mock_response = {
"request_id": "test",
"module": "sort",
"version": "1.0.0",
"status": "success",
"data": {"sorted": [1, 2, 3], "item_type": "number", "count": 3},
"error": None
}
mock_client.return_value.__aenter__.return_value.post.return_value.json.return_value = mock_response
# Your test code here
Test Fixtures¶
Define Custom Fixtures¶
import pytest
from httpx import AsyncClient, ASGITransport
from main import app
@pytest.fixture
def client():
transport = ASGITransport(app=app)
return AsyncClient(transport=transport, base_url="http://test")
@pytest.fixture
def anyio_backend():
return "asyncio"
@pytest.mark.asyncio
async def test_using_fixture(client):
response = await client.get("/health")
assert response.status_code == 200
CI/CD Integration¶
GitHub Actions Example¶
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install -r gateway/requirements.txt
- name: Run tests
run: |
cd gateway
pytest tests/ -v
Best Practices¶
- Test Edge Cases: Test empty inputs, max values, etc.
- Test Errors: Ensure error handling works correctly
- Test Integration: Test module communication
- Use Fixtures: Reuse common test setup
- Keep Tests Fast: Avoid slow external calls in unit tests