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>
rust
#[test] fn test_process_payment_insufficient_funds_returns_error() #[tokio::test] async fn test_withdraw_valid_amount_decreases_balance()
AAA Pattern (Arrange-Act-Assert)
rust
#[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
rust
// 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