autogpt_platform/backend/TESTING.md
This guide covers testing practices for the AutoGPT Platform backend, with a focus on snapshot testing for API endpoints.
The backend uses pytest for testing with the following key libraries:
pytest - Test frameworkpytest-asyncio - Async test supportpytest-mock - Mocking supportpytest-snapshot - Snapshot testing for API responsespoetry run test
poetry run pytest path/to/test_file.py
poetry run pytest -v
poetry run pytest --cov=backend
Snapshot testing captures the output of your code and compares it against previously saved snapshots. This is particularly useful for testing API responses.
snapshots/ directoriesWhen you first write a test or when the expected output changes:
poetry run pytest path/to/test.py --snapshot-update
⚠️ Important: Always review snapshot changes before committing! Use git diff to verify the changes are expected.
import json
from pytest_snapshot.plugin import Snapshot
def test_api_endpoint(snapshot: Snapshot):
response = client.get("/api/endpoint")
# Snapshot the response
snapshot.snapshot_dir = "snapshots"
snapshot.assert_match(
json.dumps(response.json(), indent=2, sort_keys=True),
"endpoint_response"
)
"user_list_response" not "response1"indent=2 for readable diffsExample of excluding dynamic data:
response_data = response.json()
# Remove dynamic fields for snapshot
response_data.pop("created_at", None)
response_data.pop("id", None)
snapshot.snapshot_dir = "snapshots"
snapshot.assert_match(
json.dumps(response_data, indent=2, sort_keys=True),
"static_response_data"
)
import json
import fastapi
import fastapi.testclient
import pytest
from pytest_snapshot.plugin import Snapshot
from backend.api.features.myroute import router
app = fastapi.FastAPI()
app.include_router(router)
client = fastapi.testclient.TestClient(app)
def test_endpoint_success(snapshot: Snapshot):
response = client.get("/endpoint")
assert response.status_code == 200
# Test specific fields
data = response.json()
assert data["status"] == "success"
# Snapshot the full response
snapshot.snapshot_dir = "snapshots"
snapshot.assert_match(
json.dumps(data, indent=2, sort_keys=True),
"endpoint_success_response"
)
For the main API routes that use JWT authentication, auth is provided by the autogpt_libs.auth module. If the test actually uses the user_id, the recommended approach for testing is to mock the get_jwt_payload function, which underpins all higher-level auth functions used in the API (requires_user, requires_admin_user, get_user_id).
If the test doesn't need the user_id specifically, mocking is not necessary as during tests auth is disabled anyway (see conftest.py).
Two global auth fixtures are provided by backend/api/conftest.py:
mock_jwt_user - Regular user with test_user_id ("test-user-id")mock_jwt_admin - Admin user with admin_user_id ("admin-user-id")These provide the easiest way to set up authentication mocking in test modules:
import fastapi
import fastapi.testclient
import pytest
from backend.api.features.myroute import router
app = fastapi.FastAPI()
app.include_router(router)
client = fastapi.testclient.TestClient(app)
@pytest.fixture(autouse=True)
def setup_app_auth(mock_jwt_user):
"""Setup auth overrides for all tests in this module"""
from autogpt_libs.auth.jwt_utils import get_jwt_payload
app.dependency_overrides[get_jwt_payload] = mock_jwt_user['get_jwt_payload']
yield
app.dependency_overrides.clear()
For admin-only endpoints, use mock_jwt_admin instead:
@pytest.fixture(autouse=True)
def setup_app_auth(mock_jwt_admin):
"""Setup auth overrides for admin tests"""
from autogpt_libs.auth.jwt_utils import get_jwt_payload
app.dependency_overrides[get_jwt_payload] = mock_jwt_admin['get_jwt_payload']
yield
app.dependency_overrides.clear()
The IDs are also available separately as fixtures:
test_user_idadmin_user_idtarget_user_id (for admin <-> user operations)def test_external_api_call(mocker, snapshot):
# Mock external service
mock_response = {"external": "data"}
mocker.patch(
"backend.services.external_api.call",
return_value=mock_response
)
response = client.post("/api/process")
assert response.status_code == 200
snapshot.snapshot_dir = "snapshots"
snapshot.assert_match(
json.dumps(response.json(), indent=2, sort_keys=True),
"process_with_external_response"
)
routes.py → routes_test.pytest_create_user_with_invalid_emaildef for FastAPI TestClient testsasync def with @pytest.mark.asyncio for testing async functions directlyAuthentication fixtures are available globally from conftest.py:
mock_jwt_user - Standard user authenticationmock_jwt_admin - Admin user authenticationconfigured_snapshot - Pre-configured snapshot fixtureCreate reusable fixtures for common test data:
@pytest.fixture
def sample_user():
return {
"email": "[email protected]",
"name": "Test User"
}
def test_create_user(sample_user, snapshot):
response = client.post("/users", json=sample_user)
# ... test implementation
All tests must use fixtures that ensure proper isolation:
The GitHub Actions workflow automatically runs tests on:
Snapshot tests work in CI by:
poetry run pytest --snapshot-update@pytest.mark.asyncioAsyncMock for mocking async functionspyproject.tomlpoetry install to ensure dependencies are installedSnapshot testing provides a powerful way to ensure API responses remain consistent. Combined with traditional assertions, it creates a robust test suite that catches regressions while remaining maintainable.
Remember: Good tests are as important as good code!