- Test the public API, not implementation details
- Include edge cases: empty inputs, boundaries, error conditions
- Performance tests for operations that must scale (10k+ entities)
- Concurrent tests for thread-safe code
# All tests
cargo test
# Specific crate
cargo test -p tensor_chain
# Specific test
cargo test test_raft_election
# With output
cargo test -- --nocapture
# Run ignored tests (slow/integration)
cargo test -- --ignoredUnit tests live in the same file:
pub fn process(data: &str) -> Result<Output> {
// implementation
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_process_valid_input() {
let result = process("valid").unwrap();
assert_eq!(result.status, "ok");
}
#[test]
fn test_process_empty_input() {
let result = process("");
assert!(result.is_err());
}
}Use the pattern: test_<function>_<scenario>_<expected>
#[test]
fn test_insert_duplicate_key_returns_error() { }
#[test]
fn test_search_empty_index_returns_empty() { }
#[test]
fn test_commit_after_abort_fails() { }For thread-safe code:
#[test]
fn test_store_concurrent_writes() {
let store = Arc::new(TensorStore::new());
let handles: Vec<_> = (0..10)
.map(|i| {
let store = Arc::clone(&store);
std::thread::spawn(move || {
for j in 0..1000 {
store.put(format!("key_{i}_{j}"), data.clone());
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
assert_eq!(store.len(), 10000);
}Mark slow tests with #[ignore]:
#[test]
#[ignore]
fn test_hnsw_search_10k_vectors() {
let mut index = HNSWIndex::new(config);
for i in 0..10_000 {
index.insert(format!("vec_{i}"), random_vector(128));
}
let start = Instant::now();
for _ in 0..100 {
index.search(&query, 10);
}
let elapsed = start.elapsed();
assert!(elapsed < Duration::from_secs(1));
}Run with: cargo test -- --ignored
Located in integration_tests/:
cargo test -p integration_testsThese test cross-crate behavior and full workflows.
Check coverage with cargo-llvm-cov:
cargo install cargo-llvm-cov
cargo llvm-cov --workspace --html
open target/llvm-cov/html/index.htmlTarget coverage thresholds:
- shell: 88%
- parser: 91%
- blob: 91%
- router: 92%
- chain: 95%
Distributed protocol changes must be verified against the TLA+
specifications in specs/tla/:
cd specs/tla
# Run TLC on all three specs
java -XX:+UseParallelGC -Xmx4g -jar tla2tools.jar \
-deadlock -workers auto -config Raft.cfg Raft.tla
java -XX:+UseParallelGC -Xmx4g -jar tla2tools.jar \
-deadlock -workers auto \
-config TwoPhaseCommit.cfg TwoPhaseCommit.tla
java -XX:+UseParallelGC -Xmx4g -jar tla2tools.jar \
-deadlock -workers auto \
-config Membership.cfg Membership.tlaWhen modifying Raft, 2PC, or gossip protocols:
- Update the corresponding
.tlaspec - Run TLC and verify zero errors
- Save output to
specs/tla/tlc-results/
See Formal Verification for background on what model checking covers.
Use trait objects for dependency injection:
pub trait Transport: Send + Sync {
fn send(&self, msg: Message) -> Result<()>;
}
// In tests
struct MockTransport {
sent: Mutex<Vec<Message>>,
}
impl Transport for MockTransport {
fn send(&self, msg: Message) -> Result<()> {
self.sent.lock().unwrap().push(msg);
Ok(())
}
}