Module 10 • Lesson 54

Testing in Rust

📚 9 min💻 Free🦀 nixus.pro

Testing in Rust

// Unit tests: in the same file as the code
fn add(a: i32, b: i32) -> i32 { a + b }

fn divide(a: f64, b: f64) -> Option {
    if b == 0.0 { None } else { Some(a / b) }
}

#[cfg(test)]           // Only compiled when running tests
mod tests {
    use super::*;      // Import everything from parent module

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
        assert_eq!(add(-1, 1), 0);
        assert_ne!(add(2, 2), 5);
    }

    #[test]
    fn test_divide() {
        assert_eq!(divide(10.0, 2.0), Some(5.0));
        assert_eq!(divide(10.0, 0.0), None);
    }

    #[test]
    #[should_panic(expected = "overflow")]
    fn test_overflow() {
        let _ = i32::MAX.checked_add(1).expect("overflow");
    }

    #[test]
    fn test_with_result() -> Result<(), String> {
        let x: i32 = "5".parse().map_err(|e: std::num::ParseIntError| e.to_string())?;
        assert_eq!(x, 5);
        Ok(())
    }
}

// Integration tests: in tests/ directory
// tests/integration_test.rs:
// use my_crate::add;
// #[test]
// fn test_add_integration() { assert_eq!(add(1, 2), 3); }

// Running tests:
// cargo test                  - all tests
// cargo test test_add         - matching name
// cargo test -- --nocapture   - show println! output
// cargo test -- --ignored     - run #[ignore]d tests

Property Testing and Benchmarks

// Property-based testing with proptest:
// proptest = "1"
use proptest::prelude::*;

proptest! {
    #[test]
    fn test_reverse_reverse(s: String) {
        let reversed: String = s.chars().rev().collect();
        let double_reversed: String = reversed.chars().rev().collect();
        prop_assert_eq!(s, double_reversed);
    }

    #[test]
    fn test_sort_idempotent(mut v: Vec) {
        v.sort();
        let first_sort = v.clone();
        v.sort();
        prop_assert_eq!(first_sort, v);
    }
}

// Benchmarks (nightly or with criterion):
// criterion = "0.5"
// use criterion::{criterion_group, criterion_main, Criterion};
// fn benchmark_add(c: &mut Criterion) {
//     c.bench_function("add", |b| b.iter(|| add(2, 3)));
// }

🎯 Practice

  1. Write unit tests for a Vec-based Stack: test push, pop, peek with empty and non-empty stacks
  2. Write an integration test for a function in your library crate
  3. Use #[should_panic] to test that your divide function panics appropriately
  4. Add proptest for a string function: verify it never panics on arbitrary input

🎉 Key Takeaways