Testing
In the previous chapters, we've looked at modularizing a Rust application. Modules are a key component for adding
unit tests to a Rust application. These modules get a special annotation, so Cargo knows that the module should be ran
during testing: #[cfg(test)]
In RustRover you can easily scaffold a test module by typing tmod
. A test function can then added by typing tfn
.
A typical unit test looks like this:
fn capitalize(value: String) -> String { value.to_uppercase() } fn main() { println!("{}", capitalize("Rust".to_string())); } #[cfg(test)] mod tests { use super::*; #[test] fn test_caps() { let input = "Rust".to_string(); let output = capitalize(input); assert_eq!("RUST", output); } }
There are three assertions available out-of-the-box that can be used during unit testing:
assert!()
assert_eq!()
assert_ne!()
You can use the play-button ▶️ in RustRover to run a single unit test, or a whole test module. Alternatively the key
combination Ctrl + r
does the same, based on the position of the cursor in the test module.
Cargo can also execute tests: cargo test
. Remember the double-ctrl
"Run Anything" operation in RustRover to
easily execute Cargo commands.
Benchmark testing
Benchmarks are slightly harder to set up, as they require an external Crate to run. We use criterion
.
First we'll include the criterion
dependency in Cargo.toml:
[dev-dependencies]
criterion = "0.3.1"
[[bench]]
name = "capitalize"
harness = false
Then we create a benches
directory that will hold our benchmark test. We'll also create a library with a single public
function: capitalize_string
, which is what we'll benchmark.
Our project structure looks like this:
|- Cargo.toml
|- benches
|- capitalize.rs
|- src
|- lib.rs
Notice that the
name
in theCargo.toml
file matches thecapitalize.rs
name in thebenches
directory.
Filename: src/lib.rs
pub fn capitalize_string(input: String) -> String {
input.to_uppercase()
}
Filename: benches/capitalize.rs
#[macro_use]
extern crate criterion;
use capitalize::capitalize_string;
use criterion::Criterion;
fn bench_caps(c: &mut Criterion) {
c.bench_function("capitalize strings", |b| {
b.iter(|| capitalize_string("rust".to_string()))
});
}
criterion_group!(benches, bench_caps);
criterion_main!(benches);
The
||
(pipe-symbols) in the benchmark code are used to create a closure. We'll look into closures in the next chapter.
You can start the test by running cargo bench
.
cargo bench
builds a release version of the application for benchmarking.
Criterion benchmarks the current run, compares the result to a previous run, and shows the performance delta.
Output from first run
capitalize strings time: [223.59 ns 225.70 ns 228.04 ns]
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
Output from second run
capitalize strings time: [219.78 ns 222.41 ns 226.20 ns]
change: [-4.7158% -3.0100% -1.3074%] (p = 0.00 < 0.05)
Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
4 (4.00%) high mild
2 (2.00%) high severe