[Rust Series 17] Testing and Documentation

한국어 버전

What You'll Learn

Testing and documentation are the most direct ways to make code trustworthy. We'll walk through the cargo test workflow, how to build #[test] functions, and how to leave runnable examples in doc comments.

Exploring cargo test

Any Rust project starts with a working cargo test setup by default.

cargo new calculator
cd calculator
cargo test

The template already includes a sample test, so you'll immediately see the runner execute. As long as nothing fails, you'll get the runtime, test count, and package name instead of an error report.

Writing #[test] Functions

Let's add a library function and a matching test to src/lib.rs.

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adds_two_numbers() {
        assert_eq!(add(2, 3), 5);
    }
}
  • The #[cfg(test)] module compiles only during cargo test, keeping the release binary slimmer.
  • Functions tagged with #[test] don't return values, and any panic marks the test as failed.

The assert_eq! macro compares the actual and expected values. When it fails, it shows both values plus the source location, which makes debugging quick.

Code inside doc comments is also a form of testing. Every documented example doubles as runnable verification, and that's one of Rust's biggest wins.

Testing Edge Cases

When you write multiple tests for the same function, choose descriptive scenario names so future refactors keep their meaning.

#[test]
fn add_handles_negative() {
    assert_eq!(add(-3, 1), -2);
}

#[test]
fn add_with_zero_returns_other() {
    assert_eq!(add(0, 7), 7);
}

If someone can infer the inputs just by reading the test names, they'll also understand failure logs more quickly.

Doc Comments and Examples

Any library you expect others to consume needs doc comments (///). Embed code blocks inside those comments, and cargo test will execute them for you.

/// Returns the sum of two integers.
///
/// # Examples
///
/// ```
/// use calculator::add;
/// assert_eq!(add(2, 2), 4);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

Doctests are lifesavers when maintaining real usage examples. If the implementation changes, outdated samples fail immediately, keeping documentation and code in sync.

cargo doc --open

Run the following command whenever you want an HTML preview of your docs:

cargo doc --open

It documents the public API of your project and opens it in the default browser. Items that aren't pub stay hidden, so be intentional about what you expose.

Common Pitfalls

  • Declaring parameters on a #[test] function causes a compile error; tests can't take arguments.
  • Async tests need a runtime such as #[tokio::test]. We focus on synchronous tests here.
  • Use cargo test -- --nocapture to see println! output; the default hides logs when tests pass.
  • If interleaved output is confusing, run cargo test -- --test-threads=1 to execute tests sequentially.

Practice

  1. Implement subtract and add two tests that cover positive and negative inputs.
  2. Execute only one test with cargo test add_handles_negative -- --nocapture and inspect the log.
  3. Add a # Panics section to the doc comments to describe when the function panics.

Wrap-Up

Tests and doc comments are the first lines of defense for a reliable Rust codebase. cargo test works out of the box, and examples inside /// comments execute as real tests. Next time, we'll run this code safely across multiple threads as we introduce concurrency basics.

💬 댓글

이 글에 대한 의견을 남겨주세요