pytest for API Testing with httpx
In this tutorial, you will learn about pytest for api testing with httpx. We cover key concepts, practical examples, and best practices to help you master this topic.
pytest with httpx provides a modern approach to testing Python APIs. httpx offers both sync and async clients, while pytest fixtures manage test setup and teardown. Together they enable fast, reliable API tests.
What You'll Learn
- pytest setup for API testing
- httpx sync and async clients
- Fixtures for test data and auth
- Parameterized tests
- Mocking external services with respx
Why It Matters
pytest is the most popular Python test framework. httpx is the modern HTTP client with async support. Combined, they provide a powerful API testing stack for Python microservices.
Real-World Use
FastAPI uses httpx TestClient internally. Django REST framework examples use pytest. Many Python Serverless APIs use pytest for Integration Testing.
flowchart LR
Test[pytest Test] --> Fixtures[Fixtures]
Fixtures --> TestClient[httpx Client]
Fixtures --> Database[Test Database]
Fixtures --> Auth[Auth Tokens]
TestClient --> App[FastAPI/Flask App]
App --> Response[Response]
TestClient --> Assert[Assertions]
Teacher Mindset
Use fixtures for shared setup (database, auth, client). Use pytest.mark.parametrize for data-driven tests. Use respx to mock external HTTP calls. Keep tests independent and idempotent.
Code Examples
# Example 1: Basic pytest with httpx
import pytest
import httpx
BASE_URL = "http://api.example.com"
def test_get_users():
response = httpx.get(f"{BASE_URL}/api/users")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
# Example 2: Async tests with fixtures
import pytest
import httpx
@pytest.fixture
async def client():
async with httpx.AsyncClient(base_url="http://test") as client:
yield client
@pytest.fixture
async def auth_token(client):
response = await client.post("/auth/login", json={
"username": "admin",
"password": "secret"
})
return response.json()["token"]
@pytest.mark.asyncio
async def test_create_user(client, auth_token):
response = await client.post(
"/api/users",
json={"name": "Alice", "email": "alice@test.com"},
headers={"Authorization": f"Bearer {auth_token}"}
)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Alice"
assert "id" in data
# Example 3: Parameterized tests with error cases
import pytest
create_user_data = [
({"name": "Alice", "email": "alice@test.com"}, 201),
({"name": ""}, 422), # Missing email
({"email": "invalid"}, 422), # Invalid email
({}, 422), # Empty body
]
@pytest.mark.parametrize("payload,expected_status", create_user_data)
@pytest.mark.asyncio
async def test_create_user_validation(client, auth_token, payload, expected_status):
response = await client.post(
"/api/users",
json=payload,
headers={"Authorization": f"Bearer {auth_token}"}
)
assert response.status_code == expected_status
Common Mistakes
- Using one client instance for all tests without isolation
- Not using async fixtures for async tests
- Mixing sync and async code without proper markers
- Hardcoding test data instead of using factories
- Not cleaning up test database state between tests
Practice
- Write a sync test for GET /api/users.
- Write an async test with a fixture for the HTTP client.
- Add a fixture that authenticates and returns a token.
- Use parametrize to test multiple input combinations.
- Challenge: Use respx to mock an external dependency in an API test.
FAQ
Mini Project
Write a pytest test suite for a Python REST API with: async httpx client fixture, auth token fixture, CRUD tests for a resource, parameterized validation tests, and database cleanup after tests.
What's Next
Next, you will learn about using the requests library for Python API testing.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro