- Add insta dev-dependency for visual regression testing - Create lib.rs to expose modules for integration tests - Add snapshot tests for progress_bar, topbar, library, help modal - Add unit tests for LibraryState navigation - Move all tests to tests/ directory (proper Rust convention) - Make build.rs skip proto compilation when protoc unavailable - Add docs/testing-possible-solutions.md with testing strategies
4.7 KiB
Testing Strategies for Ratatui TUI Applications
Research summary for testing the ui-agregator TUI application.
1. TestBackend + Buffer Assertions (Built-in)
Ratatui provides TestBackend - an in-memory terminal mock for testing without a real TTY.
use ratatui::{backend::TestBackend, Terminal};
#[test]
fn test_widget_renders() {
let mut terminal = Terminal::new(TestBackend::new(80, 24)).unwrap();
terminal.draw(|frame| {
frame.render_widget(MyWidget::new(), frame.area());
}).unwrap();
terminal.backend().assert_buffer_lines([
"Expected line 1",
"Expected line 2",
]);
}
Best for: Unit testing widgets, layout logic, pure rendering functions.
Key Methods:
TestBackend::new(width, height)- Create in-memory terminalbackend.buffer()- Access rendered bufferbackend.assert_buffer(&expected)- Assert buffer equalitybackend.assert_buffer_lines(lines)- Assert against string lines
2. Snapshot Testing with insta (Visual Regression)
Captures rendered output to .snap files; detects unexpected visual changes.
Official Recipe: https://ratatui.rs/recipes/testing/snapshots/
Setup
[dev-dependencies]
insta = "1.40.0"
cargo install cargo-insta
Usage Pattern
#[test]
fn test_library_view_snapshot() {
let mut terminal = Terminal::new(TestBackend::new(80, 40)).unwrap();
let mut state = LibraryState::new(vec![/* test data */]);
terminal.draw(|f| render_library(f, f.area(), &mut state)).unwrap();
insta::assert_snapshot!(terminal.backend());
}
Workflow
- First run: Creates
.snapfile insnapshots/directory - Subsequent runs: Compares against snapshot
- Review changes:
cargo insta review- interactive diff viewer - Update snapshots:
cargo insta test --review
Best for: Catching visual regressions, complex layouts, multi-component views.
Note: Color/style information is not currently captured in snapshots.
3. Direct Unit Tests (State Logic, No Rendering)
Test state machines and business logic without rendering.
#[test]
fn test_library_navigation() {
let mut state = LibraryState::new(test_artists());
state.move_down();
assert_eq!(state.selected_artist_index(), Some(1));
state.focus_right();
assert_eq!(state.focus, LibraryFocus::Albums);
}
Best for: State transitions, event handling logic, data transformations.
4. PTY-Based Integration Tests (ratatui-testlib)
Real terminal emulation with event simulation for end-to-end testing.
[dev-dependencies]
ratatui-testlib = "0.1"
#[test]
fn test_tab_switching_integration() -> Result<()> {
let mut harness = TuiTestHarness::new(80, 24)?;
harness.spawn(CommandBuilder::new("./ui-agregator"))?;
harness.wait_for(|s| s.contents().contains("Library"))?;
harness.send_key(KeyCode::Char('2'))?;
harness.wait_for(|s| s.contents().contains("Wanted"))?;
Ok(())
}
Best for: Full E2E scenarios, keyboard/mouse flow testing, CI validation.
5. Property-Based Testing (proptest)
Test invariants across random inputs - excellent for coordinate math and bounds checking.
[dev-dependencies]
proptest = "1.4"
proptest! {
#[test]
fn click_never_panics(x in 0u16..200, y in 0u16..100) {
let mut app = App::new();
app.size = Rect::new(0, 0, 80, 24);
app.handle_click(x, y, MouseButton::Left);
}
}
Best for: Click handling, scroll bounds, layout calculations.
Testing Plan for ui-agregator
| Component | Strategy | Priority |
|---|---|---|
LibraryState navigation |
Unit tests | High |
NotificationManager TTL/history |
Unit tests | High |
App.handle_click() routing |
Unit + proptest | High |
| Tab views (wanted, queue, history) | Snapshot tests (insta) | Medium |
progress_bar.rs |
Already has 3 tests | Done |
| Modals (help, quit) | Snapshot tests | Medium |
| gRPC response → model conversion | Unit tests | Medium |
| Full app E2E flows | ratatui-testlib | Low |
Recommended Dependencies
[dev-dependencies]
insta = "1.40.0"
rstest = "0.22" # Parameterized tests
proptest = "1.4" # Property-based testing
# ratatui-testlib = "0.1" # Optional: PTY integration tests
Resources
- Official Testing Docs: https://ratatui.rs/recipes/testing/
- Snapshot Testing Recipe: https://ratatui.rs/recipes/testing/snapshots/
- TestBackend API: https://docs.rs/ratatui/latest/ratatui/backend/struct.TestBackend.html
- insta Documentation: https://insta.rs/docs/
- ratatui-testlib: https://docs.rs/ratatui-testlib/