Testing And Benchmarking in Rust

Here’s the translation of the Go testing and benchmarking example to Rust:

Unit testing is an important part of writing principled Rust programs. The #[test] attribute and the assert! macro provide the tools we need to write unit tests, and the cargo test command runs tests.

For the sake of demonstration, this code is in the main module, but it could be any module. Testing code typically lives in the same module as the code it tests.

use std::cmp;

// We'll be testing this simple implementation of an
// integer minimum. Typically, the code we're testing
// would be in a source file named something like
// `int_utils.rs`, and the test file for it would then
// be named `int_utils_test.rs`.
fn int_min(a: i32, b: i32) -> i32 {
    cmp::min(a, b)
}

// A test is created by writing a function with the #[test] attribute.
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_int_min_basic() {
        let ans = int_min(2, -2);
        // assert! will report test failures.
        assert_eq!(ans, -2, "int_min(2, -2) = {}; want -2", ans);
    }

    // Writing tests can be repetitive, so it's idiomatic to
    // use a table-driven style, where test inputs and
    // expected outputs are listed in a table and a single loop
    // walks over them and performs the test logic.
    #[test]
    fn test_int_min_table_driven() {
        let tests = vec![
            (0, 1, 0),
            (1, 0, 0),
            (2, -2, -2),
            (0, -1, -1),
            (-1, 0, -1),
        ];

        for (a, b, want) in tests {
            let ans = int_min(a, b);
            assert_eq!(ans, want, "got {}, want {}", ans, want);
        }
    }
}

// Benchmark tests in Rust are typically created using
// external crates like `criterion`. Here's a simple example
// using the built-in test::Bencher, but note that this
// is not as sophisticated as Go's benchmarking tools.
#[cfg(test)]
mod benches {
    use super::*;
    use test::Bencher;

    #[bench]
    fn bench_int_min(b: &mut Bencher) {
        b.iter(|| {
            int_min(1, 2);
        });
    }
}

Run all tests in the current project in verbose mode:

$ cargo test -- --nocapture
running 2 tests
test tests::test_int_min_basic ... ok
test tests::test_int_min_table_driven ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Run all benchmarks in the current project. Note that benchmarks are unstable in Rust and require the nightly toolchain:

$ cargo +nightly bench
running 1 test
test benches::bench_int_min ... bench:           0 ns/iter (+/- 0)

test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in 0.00s

In Rust, benchmarking is not as integrated into the standard tooling as it is in Go. For more sophisticated benchmarking, consider using external crates like criterion or bencher.