Test Tooling
Test Tooling
The reusable test infrastructure: shared fixtures, factories, and mock builders in tests/.
Shared Fixtures
tests/conftest.py provides session-scoped fixtures used across the suite:
import pytest
@pytest.fixture
def mock_storage_adapter():
"""Mock storage adapter with async methods."""
from unittest.mock import AsyncMock, MagicMock
adapter = MagicMock()
adapter.store_chat_message = AsyncMock(return_value="msg_test_123")
adapter.get_messages = AsyncMock(return_value=[])
adapter.storage_callback = AsyncMock()
return adapter
@pytest.fixture
def mock_user():
"""Mock user with predictable pk and is_authenticated."""
...
@pytest.fixture
def mock_agents_registry():
"""Patch the global agent registry at both import paths."""
...Factories
Factories live in tests/factories/. Schema factories (schemas.py) use polyfactory; ORM factories (db.py) use factory_boy:
from tests.factories.schemas import ChatMessageFactory, ThreadInfoFactory, chat_message
msg = ChatMessageFactory.build(role="user", content="Hi")
thread = ThreadInfoFactory.build(user_id="user-1", agent_id="test-agent")
payload = chat_message(role="user", text="Hello", message_id="msg-1")
# -> Message(role="user", parts=[MessagePart(type="text", text="Hello")], ...)Django ORM factories live in tests/factories/db.py.
Mock Builders
Reusable helpers in tests/mocks/ keep tests declarative:
from tests.mocks.agent import create_agent_mock
from tests.mocks.storage import mock_get_storage, setup_thread_adapter
# Agent that looks registered but never calls a provider
agent = create_agent_mock()
# Storage adapter class registered on the registry with a ThreadInfo
setup_thread_adapter(registry, user_id="user-1", agent_id="test-agent")
# A patched get_storage / rate_message
with mock_get_storage(method="rate_message", return_value=True):
...from tests.mocks.permissions import thread_permissions
# Patch permission resolution for a test
with thread_permissions("apps.agents.permissions.DemoThreadPermission"):
...Mocking the model generator
Never construct a real OpenAIChatGenerator. Use a stub object with the shape Stream/Run expect:
class FakeGenerator:
streaming_callback = None
def run(self, *, messages, generation_kwargs=None):
return {"replies": [FakeReply(text='{"title": "Test"}')]}Mocking storage adapters
from unittest.mock import AsyncMock, MagicMock
storage = MagicMock()
storage.store_chat_message = AsyncMock(return_value="msg_1")
storage.get_messages = AsyncMock(return_value=[])Next: CLI, management command implementation.