Quality Unit Testing for Rust
Write tests that catch real bugs and provide deployment confidence.
Core Principles
Quality over coverage: Tests should catch real bugs, not just boost percentages.
Quick Reference
Naming: test_<function><scenario><expected>
#[test] fn test_process_payment_insufficient_funds_returns_error()
#[tokio::test] async fn test_withdraw_valid_amount_decreases_balance()
AAA Pattern (Arrange-Act-Assert)
#[test] fn test_account_withdraw_decreases_balance() { // Arrange let mut account = Account::new(100);
// Act
let result = account.withdraw(30);
// Assert
assert!(result.is_ok());
assert_eq!(account.balance(), 70);
}
Isolation
✓ Mock: APIs, databases, file systems, external services ✗ Don't mock: Value types, pure functions, code under test
Single Responsibility
Each test verifies ONE behavior with ONE reason to fail.
Rust-Specific Patterns
// Async tests #[tokio::test] async fn test_async_operation() { /* ... */ }
// Result-based tests #[test] fn test_operation() -> anyhow::Result<()> { /* ... */ }
// Test builders let episode = TestEpisodeBuilder::new() .with_task("Test task") .completed(true) .build();
// RAII cleanup struct TestDb(TempDir); impl Drop for TestDb { fn drop(&mut self) { /* auto cleanup */ } }
Success Metrics
✓ Deploy without manual testing ✓ Test failures pinpoint exact problems ✓ Refactoring doesn't break unrelated tests ✓ Tests run in milliseconds
Workflow
Creating Tests:
-
Understand the code behavior
-
Identify risks
-
Write failing test first (red-green-refactor)
-
Apply AAA pattern
-
Isolate dependencies
-
Verify speed (milliseconds)
Reviewing Tests:
-
Run analysis script
-
Check naming conventions
-
Ensure isolation
-
Confirm single responsibility