Rust Tutorial · Projects and Testing · Lesson 28 of 29
Rust Testing
Write unit tests with #[test] and assertions, test errors and panics, and run them with cargo test.
Unit tests
Tests live next to the code in a #[cfg(test)] module and use assert!, assert_eq! and assert_ne!.
cargo test compiles and runs them; a failing assertion shows the expected and actual values, as in the third
test below, which exposes a rounding decision the code didn't make deliberately.
pub fn apply_discount(price_cents: u64, percent: u8) -> Result<u64, String> {
if percent > 100 {
return Err(format!("invalid discount {percent}%"));
}
Ok(price_cents - price_cents * percent as u64 / 100)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ten_percent_off() {
assert_eq!(apply_discount(2_000, 10), Ok(1_800));
}
#[test]
fn rejects_more_than_100_percent() {
assert!(apply_discount(2_000, 150).is_err());
}
#[test]
fn half_price_rounds_down() {
// fails: the integer maths rounds 499.5 cents of discount down, so the price is 500, not 499
assert_eq!(apply_discount(999, 50), Ok(499), "half of 9.99 should round in the customer's favour");
}
}
Output
running 3 tests
test tests::half_price_rounds_down ... FAILED
test tests::rejects_more_than_100_percent ... ok
test tests::ten_percent_off ... ok
failures:
---- tests::half_price_rounds_down stdout ----
thread 'tests::half_price_rounds_down' panicked at main.rs:25:9:
assertion `left == right` failed: half of 9.99 should round in the customer's favour
left: Ok(500)
right: Ok(499)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::half_price_rounds_down
test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in <time>Run this on your computer — see “Get Started” for setup.
Integration tests go in a tests/ directory and use your crate like an external user; documentation examples in
/// comments are run as tests too.
Test yourself
Which attribute marks a function as a test?
#[unit]#[test]#[cfg(test)]@Test
Show answer
Answer: #[test]
#[cfg(test)] only compiles the module in test builds; #[test] marks each test.