Testing¶
Test Structure¶
tests/
├── conftest.py # Shared fixtures
├── unit/ # Fast, isolated tests
│ ├── test_config.py
│ ├── test_router.py
│ ├── test_transcribe.py
│ └── test_permissions.py
├── integration/ # Tests with mocked services
│ ├── test_session_manager.py
│ ├── test_bot_handlers.py
│ └── test_whisper_client.py
└── e2e/ # Full flow tests
└── test_voice_flow.py
Running Tests¶
# All tests
just test
# By category
just test-unit # Fast, no external deps
just test-integration # Needs mocked services
just test-e2e # Full flow tests
# With coverage
just test-coverage
Test Markers¶
@pytest.mark.unit # Fast unit tests
@pytest.mark.integration # Integration tests
@pytest.mark.e2e # End-to-end tests
@pytest.mark.slow # Long-running tests
Fixtures¶
Configuration¶
@pytest.fixture
def mock_settings() -> Settings:
"""Test settings with safe defaults."""
return Settings(
telegram_bot_token="test-token",
allowed_chat_ids="123,456",
)
Telegram Mocks¶
@pytest.fixture
def mock_telegram_update() -> MagicMock:
"""Fake Telegram Update with voice message."""
@pytest.fixture
def mock_telegram_context() -> MagicMock:
"""Fake context with file download."""
HTTP Mocking¶
async def test_transcription(httpx_mock: HTTPXMock):
httpx_mock.add_response(
url="http://localhost:8080/transcribe",
json={"text": "hello world"},
)
result = await transcribe(b"audio", "http://localhost:8080/transcribe")
assert result == "hello world"
Writing Tests¶
Unit Tests¶
Test single functions in isolation:
@pytest.mark.unit
def test_parse_approve_command():
result = parse_command("yes")
assert result.command_type == CommandType.APPROVE
Integration Tests¶
Test component interactions with mocks:
@pytest.mark.integration
async def test_session_creation(session_manager):
session = session_manager.get_or_create(123)
assert session.chat_id == 123
End-to-End Tests¶
Test complete flows:
@pytest.mark.e2e
async def test_voice_flow(e2e_bot, httpx_mock):
httpx_mock.add_response(json={"text": "status"})
# Simulate full voice message handling
await e2e_bot.handle_voice(update, context)
Coverage¶
Target: 80%+ line coverage
just test-coverage
# Opens htmlcov/index.html