Skip to content

Rust Testing

Rust has testing built directly into the language and cargo, so you don’t need to reach for a separate library to get started. This page covers just enough to write and run your first tests.

A test is a regular function marked with #[test]:

fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_two_numbers() {
assert_eq!(add(2, 3), 5);
}
}

A few things worth pointing out:

  • #[cfg(test)] tells Rust this module is only compiled when running tests — it’s not part of your normal program.
  • use super::*; brings in everything from the parent module (here, the add function), so the test can call it.
  • #[test] marks a specific function as a test that cargo test should run.

From your project folder:

Terminal window
cargo test

You’ll see output like:

running 1 test
test tests::it_adds_two_numbers ... ok
test result: ok. 1 passed; 0 failed; 0 ignored

Three macros cover most of what you’ll need:

#[test]
fn checks() {
assert!(2 + 2 == 4); // fails the test if the condition is false
assert_eq!(2 + 2, 4); // fails if the two values aren't equal
assert_ne!(2 + 2, 5); // fails if the two values ARE equal
}

assert_eq! and assert_ne! are usually preferred over a plain assert!(a == b) — when they fail, they print both values, so you can immediately see what didn’t match instead of just “assertion failed.”

fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn this_will_fail() {
assert_eq!(add(2, 2), 5);
}
}
test tests::this_will_fail ... FAILED
assertion `left == right` failed
left: 4
right: 5

That’s exactly the kind of message you want when a test fails — it tells you what your code actually produced versus what the test expected.

Sometimes the correct behavior is a panic — say, a function that should refuse invalid input. #[should_panic] tests for that:

fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("cannot divide by zero");
}
a / b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn dividing_by_zero_panics() {
divide(10, 0);
}
}

The honest answer: for a short script, you probably won’t. Tests start paying off once code is going to be reused or changed later — they let you refactor with confidence, because cargo test will tell you immediately if you broke something, instead of finding out from a user.

  • Mark a function #[test] to make it a test; run all tests with cargo test.
  • assert!, assert_eq!, and assert_ne! cover most everyday checks — prefer assert_eq!/assert_ne! for better failure messages.
  • #[should_panic] tests that a function panics when it’s supposed to.
  • Tests are most valuable for code you expect to change or reuse later.

Quick check

1. What attribute marks a function as a test that cargo test should run?

2. Why are assert_eq! and assert_ne! usually preferred over a plain assert!(a == b)?

3. What does #[should_panic] do?

Score: 0 / 3