← Back to Rust Course | Chapter 15: Cargo, Testing & Best Practices | Lesson 3 of 8

Writing Unit Tests

Unit tests are small checks you write to make sure a specific piece of your code keeps doing exactly what you expect.
Syntax
rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn name() {
        assert_eq!(actual, expected);
    }
}

Marking a Function as a Test

The #[test] attribute tells Cargo's test runner that a function is a test case, which it will execute and report the result of when you run cargo test.

Example: Marking a Function as a Test

markup
// Define the function `add` taking `a`, `b` and returning an integer
fn add(a: i32, b: i32) -> i32 {
    a + b
}

// Mark the following function as a unit test
#[test]
// Define the function `test_add` with no parameters
fn test_add() {
    // Assert that add(2, 3), 5 — panics if this is false
    assert_eq!(add(2, 3), 5);
}

// Entry point: execution of the program starts here
fn main() {
    // Print "add(2, 3) = {}" to the console, substituting in `add(2, 3)` (with a trailing newline)
    println!("add(2, 3) = {}", add(2, 3));
}

Organizing Tests in a Module

Convention places unit tests in a nested module annotated with #[cfg(test)], so the test code is compiled only when running tests, not included in regular release builds.

Example: Organizing Tests in a Module

markup
// Define the function `multiply` taking `a`, `b` and returning an integer
fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

// Attribute: #[cfg(test)]
#[cfg(test)]
// Declare a module named `tests`
mod tests {
    // Bring `super::*` into scope
    use super::*;

    // Mark the following function as a unit test
    #[test]
    // Define the function `test_multiply` with no parameters
    fn test_multiply() {
        // Assert that multiply(3, 4), 12 — panics if this is false
        assert_eq!(multiply(3, 4), 12);
    }
}

// Entry point: execution of the program starts here
fn main() {
    // Print "multiply(3, 4) = {}" to the console, substituting in `multiply(3, 4)` (with a trailing newline)
    println!("multiply(3, 4) = {}", multiply(3, 4));
}

Running Tests with cargo test

cargo test compiles the project in test mode, discovers every function marked #[test], runs them all, and prints a pass/fail summary.

Example: Running Tests with cargo test

bash
cargo test

⚠️ Run this in your own terminal or Node.js environment.

Testing Multiple Cases

A function is often exercised by several test cases covering typical inputs, edge cases, and boundary conditions, each as its own #[test] function.

Example: Testing Multiple Cases

markup
// Define the function `is_even` taking `n` and returning a boolean
fn is_even(n: i32) -> bool {
    n % 2 == 0
}

// Mark the following function as a unit test
#[test]
// Define the function `test_even_number` with no parameters
fn test_even_number() {
    // Assert that is_even(4) — panics if this is false
    assert!(is_even(4));
}

// Mark the following function as a unit test
#[test]
// Define the function `test_odd_number` with no parameters
fn test_odd_number() {
    // Assert that !is_even(7) — panics if this is false
    assert!(!is_even(7));
}

// Entry point: execution of the program starts here
fn main() {
    // Print "is_even(4) = {}" to the console, substituting in `is_even(4)` (with a trailing newline)
    println!("is_even(4) = {}", is_even(4));
    // Print "is_even(7) = {}" to the console, substituting in `is_even(7)` (with a trailing newline)
    println!("is_even(7) = {}", is_even(7));
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting the #[test] attribute above a test function, which means cargo test will not recognize or run it.
  2. Writing test functions outside of a #[cfg(test)] module, causing them to be compiled into the regular release binary unnecessarily.
  3. Testing implementation details that change often instead of testing the public, stable behavior of a function.
Chapter Summary
  • #[test] marks a function as a test that cargo test will discover and run.
  • Tests are conventionally placed inside a #[cfg(test)] mod tests { ... } block, keeping them separate from release builds.
  • assert_eq!, assert!, and assert_ne! are the primary macros used to check expected outcomes inside tests.
  • cargo test compiles and runs every discovered test, reporting pass/fail results for each.
🔒

Chapter Quiz — Complete all 8 topics to unlock

0/8 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.