Backend Testing Tools and Libraries in Shadowbroker: A Complete Guide
Shadowbroker uses pytest, pytest-asyncio, and httpx to create an asynchronous testing environment for its FastAPI backend, enabling in-process HTTP testing without launching real servers.
The Shadowbroker repository implements a Python FastAPI service that relies on a comprehensive testing stack defined in its project metadata. Understanding the backend testing tools and libraries in Shadowbroker provides insight into how the codebase achieves fast, deterministic validation of API endpoints while maintaining full integration coverage. The configuration found in backend/pyproject.toml and the test suite within backend/tests/ demonstrate modern patterns for async Python web application testing.
Core Testing Libraries
pytest and pytest-asyncio
pytest serves as the primary test runner and assertion framework, declared as a development dependency in backend/pyproject.toml. The pytest-asyncio plugin extends this capability to support asynchronous test functions, enabling async def fixtures and test cases throughout the suite. This combination allows the test runner to properly handle the async/await patterns inherent to FastAPI applications.
httpx with ASGITransport
httpx provides the HTTP client for integration tests, utilized with ASGITransport to create an in-process bridge to the FastAPI application. The client fixture in backend/tests/conftest.py constructs an httpx.AsyncClient using ASGITransport(app=app), allowing tests to invoke endpoints like client.get("/api/health") without spinning up a real network stack or external server.
FastAPI and Pydantic
While FastAPI is the web framework under test, Pydantic handles request and response schema validation. The test suite verifies that invalid inputs automatically trigger 422 validation errors, as demonstrated in test_api_smoke.py’s TestQueryValidation class.
unittest.mock for Service Isolation
The standard library's unittest.mock patches background services to prevent non-deterministic side effects. Fixtures in conftest.py suppress schedulers and streamers (such as services.data_fetcher.start_scheduler and services.ais_stream.start_ais_stream) to maintain a deterministic test environment.
Test Configuration and Structure
Dependency Management
All testing dependencies are centralized in backend/pyproject.toml, cleanly separating runtime requirements (httpx, fastapi, pydantic) from development tools (pytest, pytest-asyncio, ruff, black). This declarative approach ensures consistent environments across development and CI/CD pipelines.
pytest.ini Discovery Rules
The backend/pytest.ini file configures test discovery patterns, ensuring pytest locates all test modules within the backend/tests/ directory and applies the appropriate async test handling.
Global Fixtures in conftest.py
The backend/tests/conftest.py file contains global fixtures that instantiate the HTTP client and suppress background tasks. The client fixture wraps httpx.AsyncClient with ASGITransport over the FastAPI app instance imported from backend/main.py.
Testing Patterns and Code Examples
Creating an In-Process Test Client
The test suite implements a synchronous wrapper around httpx's async client to simplify test writing. This pattern in backend/tests/conftest.py handles the event loop management internally:
# backend/tests/conftest.py
@pytest.fixture()
def client(_suppress_background_services):
from httpx import ASGITransport, AsyncClient
from main import app
transport = ASGITransport(app=app)
class SyncClient:
def __init__(self):
self._loop = asyncio.new_event_loop()
self._transport = ASGITransport(app=app)
def get(self, url, **kw):
return self._loop.run_until_complete(self._get(url, **kw))
async def _get(self, url, **kw):
async with AsyncClient(transport=self._transport, base_url="http://test") as ac:
return await ac.get(url, **kw)
# post / put / delete follow the same pattern …
return SyncClient()
Endpoint Smoke Testing
Basic health checks verify that the FastAPI application responds correctly to HTTP requests:
# backend/tests/test_api_smoke.py
class TestHealthEndpoint:
def test_health_returns_200(self, client):
r = client.get("/api/health")
assert r.status_code == 200
data = r.json()
assert data["status"] == "ok"
Validation Error Testing
The integration tests verify that Pydantic returns proper HTTP 422 responses for malformed requests:
# backend/tests/test_api_smoke.py
class TestQueryValidation:
def test_region_dossier_rejects_invalid_lat(self, client):
r = client.get("/api/region-dossier?lat=999&lng=0")
assert r.status_code == 422
Mocking Background Services
Fixtures use context managers to patch services that should not execute during tests:
# backend/tests/conftest.py
with (
patch("services.data_fetcher.start_scheduler"),
patch("services.ais_stream.start_ais_stream"),
# …other patches…
):
yield
Summary
- pytest and pytest-asyncio provide the async-capable test runner and assertion framework for the Python backend.
- httpx with
ASGITransportenables in-process HTTP testing against the FastAPI application without network overhead. - unittest.mock isolates the test environment by suppressing background services like schedulers and data streams.
- Configuration files
backend/pyproject.tomlandbackend/pytest.inicentralize dependencies and discovery rules. - The
clientfixture inbackend/tests/conftest.pydemonstrates the synchronous wrapper pattern for async HTTP clients.
Frequently Asked Questions
What is the primary test runner used in Shadowbroker?
pytest is the primary test runner, configured via backend/pyproject.toml and backend/pytest.ini. It discovers and executes all test modules in the backend/tests/ directory, providing assertion methods and reporting capabilities for the FastAPI backend testing suite.
How does Shadowbroker handle asynchronous testing?
The project uses pytest-asyncio as a development dependency to enable async def test functions and fixtures. This allows the test suite to properly await coroutines when testing the asynchronous FastAPI endpoints, particularly when using httpx.AsyncClient for HTTP requests.
Why does Shadowbroker use httpx instead of the standard TestClient?
Shadowbroker uses httpx with ASGITransport rather than FastAPI's TestClient because it provides direct async support and greater flexibility for integration testing. The client fixture in conftest.py wraps httpx.AsyncClient to create an in-process bridge to the FastAPI app at base_url="http://test", eliminating the need for a real server while maintaining full HTTP semantics.
Where are the test dependencies configured in the repository?
All test dependencies are declared in backend/pyproject.toml under the development dependencies section. This includes pytest, pytest-asyncio, httpx, and code quality tools like ruff and black. The test discovery patterns are further configured in backend/pytest.ini.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →