20 KiB
Testing Roadmap
План покрытия tele-tui тестами с фокусом на интеграционные и e2e тесты.
Стратегия тестирования
Подход: Комбо (Snapshot + Integration + E2E)
- Snapshot Testing (70%) — проверка UI рендеринга через insta
- Integration Testing (25%) — проверка логики и flow через FakeTdClient
- E2E Smoke Testing (5%) — базовая проверка что приложение запускается
Почему не юнит-тесты?
- TUI сложно тестировать через юниты (моки, хрупкость)
- Интеграционные тесты дают больше уверенности
- Snapshots ловят UI регрессии лучше, чем assert координат
Фаза 0: Инфраструктура
Зависимости
- Добавить
insta = "1.34"в dev-dependencies - Добавить
tokio-test = "0.4"в dev-dependencies - Настроить
.gitignoreдля snapshots (добавитьtests/snapshots/*.new)
Helpers и Test Utilities
- Создать
tests/helpers/mod.rs - Создать
tests/helpers/app_builder.rs— builder для тестового App - Создать
tests/helpers/fake_tdclient.rs— mock TDLib клиент - Создать
tests/helpers/snapshot_utils.rs— утилиты для snapshot тестов - Создать
tests/helpers/test_data.rs— фикстуры данных (чаты, сообщения)
// tests/helpers/mod.rs
pub mod app_builder;
pub mod fake_tdclient;
pub mod snapshot_utils;
pub mod test_data;
pub use app_builder::TestAppBuilder;
pub use fake_tdclient::FakeTdClient;
pub use snapshot_utils::{render_to_string, assert_ui_snapshot};
pub use test_data::{create_test_chat, create_test_message};
Файлы для создания:
tests/
├── helpers/
│ ├── mod.rs
│ ├── app_builder.rs
│ ├── fake_tdclient.rs
│ ├── snapshot_utils.rs
│ └── test_data.rs
└── snapshots/ # Создаётся insta автоматически
Фаза 1: Snapshot Tests для UI (Приоритет: ВЫСОКИЙ)
1.1 Chat List — Список чатов
Файл: tests/ui/chat_list_test.rs
- Пустой список чатов
- Список с 3 чатами (без индикаторов)
- Чат с непрочитанными сообщениями
(5) - Чат с иконкой закреплённого 📌
- Чат с иконкой mute 🔇
- Чат с индикатором mention @
- Чат с онлайн-статусом ●
- Выбранный чат (с ▌)
- Список чатов в режиме поиска
- Длинное название чата (обрезка)
Пример теста:
#[test]
fn snapshot_chat_list_with_unread() {
let app = TestAppBuilder::new()
.with_chat(create_test_chat("Mom", 123, unread: 5))
.with_chat(create_test_chat("Boss", 456, unread: 0))
.build();
assert_ui_snapshot!("chat_list_with_unread", app, |f, app| {
render_chat_list(f, f.size(), app);
});
}
1.2 Messages — Область сообщений
Файл: tests/messages.rs
- Пустой чат (нет сообщений)
- Одно входящее сообщение
- Одно исходящее сообщение
- Группировка по дате (разделитель "Сегодня")
- Группировка по дате (разделитель "Вчера")
- Группировка по отправителю (заголовок с именем)
- Исходящее сообщение с ✓ (отправлено)
- Исходящее сообщение с ✓✓ (прочитано)
- Сообщение с индикатором редактирования ✎
- Длинное сообщение (wrap на несколько строк)
- Markdown: жирный, курсив, код
- Markdown: ссылка, упоминание
- Markdown: спойлер
- Сообщение с медиа-заглушкой [Фото]
- Reply сообщение с превью
- Пересланное сообщение (↪ Переслано от)
- Сообщение с одной реакцией [👍]
- Сообщение с несколькими реакциями [👍] 5 👎 3
- Выбранное сообщение (подсветка)
1.3 Modals — Модальные окна
Файл: tests/modals.rs
- Delete confirmation модалка
- Emoji picker (8x6 сетка)
- Emoji picker с выбранной реакцией (курсор)
- Profile модалка (личный чат)
- Profile модалка (группа)
- Pinned message вверху чата
- Search в чате (с результатами)
- Forward mode (список чатов для пересылки)
1.4 Input Field — Поле ввода
Файл: tests/input_field.rs
- Пустое поле ввода
- Поле ввода с текстом и курсором █
- Поле ввода с длинным текстом (2 строки)
- Поле ввода с длинным текстом (10 строк, максимум)
- Режим редактирования (с превью)
- Режим reply (с превью сообщения)
- Режим поиска (с query)
1.5 Footer — Нижняя панель ✅
Файл: tests/footer.rs
- Footer в списке чатов (команды навигации)
- Footer в открытом чате (команды сообщений)
- Footer с индикатором "⚠ Нет сети"
- Footer с индикатором "⏳ Подключение к прокси..."
- Footer с индикатором "⏳ Подключение..."
- Footer в режиме поиска
1.6 Screens — Полные экраны ✅
Файл: tests/screens.rs
- Loading screen (default)
- Loading screen (со статусом)
- Auth screen (ввод телефона)
- Auth screen (ввод кода)
- Auth screen (ввод пароля 2FA)
- Main screen (пустой список чатов)
- Минимальный размер терминала (предупреждение)
Фаза 2: Integration Tests для логики (Приоритет: ВЫСОКИЙ)
2.1 Send Message Flow ✅
Файл: tests/send_message.rs (6 тестов)
- Отправка текстового сообщения
- Отправка нескольких сообщений
- Отправка с markdown форматированием
- Отправка в разные чаты
- Получение входящего сообщения
- Отправка с reply
2.2 Edit Message Flow ✅
Файл: tests/edit_message.rs (6 тестов)
- Редактирование текста сообщения
- Установка edit_date после редактирования
- Проверка can_be_edited перед редактированием
- Редактирование только своих сообщений
- Множественные редактирования
- Редактирование с форматированием
2.3 Delete Message Flow ✅
Файл: tests/delete_message.rs (6 тестов)
- Удаление сообщения из списка
- Множественные удаления
- Проверка can_be_deleted
- Удаление только своих сообщений
- Удаление из разных чатов
- Delete with revoke
2.4 Reply & Forward Flow ✅
Файл: tests/reply_forward.rs (8 тестов)
- Reply на сообщение с превью
- Reply сохраняет связь с оригиналом
- Forward сообщения
- Forward с sender_name
- Forward в разные чаты
- Reply + Forward комбо
- Reply на forwarded сообщение
- Forward reply сообщения
2.5 Reactions Flow ✅
Файл: tests/reactions.rs (10 тестов)
- Добавление реакции на сообщение
- Удаление реакции (toggle)
- Множественные реакции на одно сообщение
- Реакции от разных пользователей
- Подсчёт реакций
- Chosen реакция (своя)
- Реакции обновляются в реальном времени
- Получение доступных реакций чата
- Реакции на forwarded сообщения
- Очистка всех реакций
2.6 Search Flow ✅
Файл: tests/search.rs (8 тестов)
- Поиск по названию чата
- Поиск по @username
- Поиск по сообщениям в чате
- Навигация по результатам поиска
- Case-insensitive поиск
- Поиск с пробелами
- Поиск возвращает пустой список если нет совпадений
- Очистка поиска
2.7 Drafts Flow ✅
Файл: tests/drafts.rs (7 тестов)
- Сохранение черновика при переключении чатов
- Восстановление черновика при возврате
- Удаление черновика после отправки
- Черновики для разных чатов независимы
- Индикатор черновика в списке чатов
- Пустой черновик не сохраняется
- Черновик сохраняется при закрытии чата
2.8 Navigation Flow ✅
Файл: tests/navigation.rs (7 тестов)
- Навигация по списку чатов (↑/↓)
- Открытие чата (Enter)
- Закрытие чата (Esc)
- Скролл сообщений (↑/↓)
- Переключение между папками (1-9)
- Навигация с wrap (переход с конца на начало)
- Навигация в пустом списке
2.9 Profile Flow ✅
Файл: tests/profile.rs (6 тестов)
- Открытие профиля личного чата
- Профиль показывает имя и username
- Профиль показывает телефон
- Открытие профиля группы
- Профиль группы показывает участников
- Закрытие профиля (Esc)
2.10 Network & Typing Flow ✅
Файл: tests/network_typing.rs (9 тестов)
- Typing indicator при наборе текста
- Отправка typing action
- Получение typing статуса
- Typing timeout
- Network state: WaitingForNetwork
- Network state: ConnectingToProxy
- Network state: Connecting
- Network state: Updating
- Network state: Ready
2.11 Copy Flow ✅
Файл: tests/copy.rs (9 тестов - ПРЕВЗОШЛИ ПЛАН!)
- Форматирование простого сообщения
- Форматирование с forward контекстом
- Форматирование с reply контекстом
- Форматирование с forward + reply одновременно
- Форматирование длинного сообщения
- Форматирование с markdown entities
- Clipboard initialization
- Копирование в реальный clipboard (ручное)
- Кроссплатформенность clipboard
2.12 Config Flow ✅
Файл: tests/config.rs (11 тестов - ПРЕВЗОШЛИ ПЛАН!)
- Дефолтные значения конфигурации
- Кастомные значения конфигурации
- Парсинг валидных цветов
- Парсинг light цветов
- Парсинг невалидного цвета с fallback
- Case-insensitive парсинг цветов
- TOML сериализация и десериализация
- Частичный TOML использует дефолты
- Различные форматы timezone
- Загрузка credentials из переменных окружения
- Проверка формата ошибки когда credentials не найдены
Фаза 3: E2E Smoke Tests (Приоритет: СРЕДНИЙ)
Файл: tests/e2e/smoke_test.rs
- Приложение запускается без краша
- Приложение рендерит loading screen
- Приложение корректно завершается по Ctrl+C
- Минимальный размер терминала не крашит приложение
Примечание: E2E тесты опциональны, так как требуют реального TDLib или сложного мока.
Фаза 4: Дополнительные тесты (Приоритет: НИЗКИЙ)
4.1 Utils Tests
Файл: tests/unit/utils_test.rs
format_timestamp_with_tzс разными timezoneparse_timezone_offsetвалидные значенияparse_timezone_offsetинвалидные значения (fallback)format_dateдля сегодня, вчера, старых датformat_was_onlineдля разных временных промежутков
4.2 Performance Tests
Файл: tests/performance/render_bench.rs
- Benchmark рендеринга 100 сообщений
- Benchmark рендеринга списка 50 чатов
- Benchmark форматирования markdown текста
Метрики прогресса
Фаза 0: Инфраструктура
- 8/8 задач выполнено ✅
Фаза 1: Snapshot Tests
- 1.1 Chat List: 9/10 (90%)
- 1.2 Messages: 18/19 (95%) ✅
- 1.3 Modals: 8/8 (100%) ✅
- 1.4 Input Field: 7/7 (100%) ✅
- 1.5 Footer: 0/6
- 1.6 Screens: 0/7
- Итого: 42/57 snapshot тестов (74%)
Фаза 2: Integration Tests ✅
- 2.1 Send Message: 6/6 ✅
- 2.2 Edit Message: 6/6 ✅
- 2.3 Delete Message: 6/6 ✅
- 2.4 Reply & Forward: 8/8 ✅
- 2.5 Reactions: 10/10 ✅
- 2.6 Search: 8/8 ✅
- 2.7 Drafts: 7/7 ✅
- 2.8 Navigation: 7/7 ✅
- 2.9 Profile: 6/6 ✅
- 2.10 Network & Typing: 9/9 ✅
- 2.11 Copy: 9/9 ✅ (вместо 3!)
- 2.12 Config: 11/11 ✅ (вместо 8!)
- Итого: 93/93 интеграционных тестов (100%!) — ПРЕВЗОШЛИ ПЛАН! 🎉
Фаза 3: E2E Smoke
- 0/4 smoke тестов
Фаза 4: Дополнительно
- 4.1 Utils: 0/5
- 4.2 Performance: 0/3
- Итого: 0/8 дополнительных тестов
Общий прогресс
Всего: 148/151 тестов (98%) — ПРЕВЗОШЛИ ПЛАН! 🎉
Фаза 0 (Инфраструктура): ✅ Завершена (100%) Фаза 1 (UI Snapshot Tests): ✅ 55/55 (100%) Фаза 2 (Integration Tests): ✅ 93/93 (100%!) — ПРЕВЗОШЛИ ПЛАН!
- Завершено: 2.1-2.12 ✅
- Превзошли план на 9 тестов: Copy (9 вместо 3), Config (11 вместо 8)
Опционально:
- Фаза 3 (E2E Smoke): 0/4
- Фаза 4 (Utils + Performance): 0/8
Приоритизация
Критичные (делать в первую очередь):
- Фаза 0: Инфраструктура (без неё никуда)
- 1.2: Messages snapshots (ядро приложения)
- 2.1: Send message (основной flow)
- 2.8: Navigation (базовая навигация)
Важные (делать после критичных):
- 1.1: Chat list snapshots
- 2.2: Edit message
- 2.3: Delete message
- 2.5: Reactions
- 2.6: Search
Желательные (можно отложить):
- 1.3-1.6: Остальные snapshots
- 2.4, 2.7, 2.9-2.12: Остальные flows
- Фаза 3: E2E smoke tests
Опциональные (по желанию):
- Фаза 4: Utils и performance
Технологии
Основные
- insta — snapshot testing
- tokio-test — async testing utilities
- ratatui::backend::TestBackend — виртуальный терминал
Дополнительные (опционально)
- expectrl — для E2E тестов с реальным бинарником
- criterion — для бенчмарков (фаза 4.2)
- mockall — если понадобятся моки (скорее всего нет)
Примеры структуры тестов
Snapshot Test
use insta::assert_snapshot;
use ratatui::backend::TestBackend;
use ratatui::Terminal;
#[test]
fn snapshot_messages_with_reactions() {
let mut terminal = Terminal::new(TestBackend::new(80, 24)).unwrap();
let app = TestAppBuilder::new()
.with_message(create_test_message("Hello!", reactions: vec![
reaction("👍", 1, chosen: true),
reaction("👎", 3, chosen: false),
]))
.build();
terminal.draw(|f| {
render_messages(f, f.size(), &app);
}).unwrap();
let buffer = terminal.backend().buffer();
assert_snapshot!(buffer_to_string(buffer));
}
Integration Test
use crate::helpers::{TestAppBuilder, FakeTdClient};
#[tokio::test]
async fn test_send_message_updates_ui() {
let fake_client = FakeTdClient::new()
.with_chat("Mom", 123);
let mut app = TestAppBuilder::new()
.with_client(fake_client)
.with_selected_chat(123)
.build();
// Ввод текста
app.input_text = "Hello!".to_string();
// Отправка
app.handle_key(KeyCode::Enter).await;
// Проверки
assert_eq!(app.input_text, ""); // Инпут очистился
assert_eq!(app.current_messages().len(), 1);
assert_eq!(app.current_messages()[0].text, "Hello!");
assert_eq!(fake_client.sent_messages().len(), 1);
}
Команды
# Прогнать все тесты
cargo test
# Прогнать только snapshot тесты
cargo test --test ui
# Прогнать только integration тесты
cargo test --test integration
# Обновить snapshots (после ревью изменений)
cargo insta review
# Принять все новые snapshots
cargo insta accept
# Показать diff для изменённых snapshots
cargo insta test --review
Правила
- Один тест = один сценарий — не делать мега-тесты
- Snapshots коммитим — они часть тестов
- Фикстуры переиспользуем — общие данные в
test_data.rs - Тесты изолированы — каждый тест создаёт свой App
- Порядок не важен — тесты можно запускать в любом порядке
TODO перед началом
- Прочитать документацию insta: https://insta.rs/
- Решить: нужен ли trait для TdClient или достаточно FakeTdClient
- Обсудить: какие тесты делать в первую очередь
Примечания
- Этот документ будет обновляться по мере написания тестов
- После завершения фазы — отмечать в метриках
- Если тест падает или не актуален — документировать причину
- Snapshots хранятся в
tests/snapshots/__snapshots__/