Introduction
Rust has a testing framework built directly into the language and Cargo, so you don't need a third-party library to write and run tests. Functions annotated with the #[test] attribute become test cases, and cargo test compiles the project in test mode and runs every test, reporting pass/fail results. Rust distinguishes between unit tests, which live alongside the code they test, and integration tests, which live in a separate top-level tests/ directory and exercise the crate's public API as an external user would.
Cricket analogy: Cargo's built-in testing is like the BCCI having its own official umpiring system rather than hiring an outside agency; #[test] functions are like scheduled practice matches, and cargo test runs the full nets session and reports who's match-fit.
Syntax
#[test]
fn it_works() {
assert!(true);
}
#[test]
fn checks_equality() {
assert_eq!(2 + 2, 4);
assert_ne!(2 + 2, 5);
}
#[test]
#[should_panic]
fn it_panics() {
panic!("expected failure");
}Explanation
The #[test] attribute marks a function as a test case; cargo test discovers and runs every such function, printing ok or FAILED for each along with a summary. The assert! macro panics (failing the test) if its argument is false; assert_eq! and assert_ne! compare two values and panic with a helpful diff-style message if they are unexpectedly equal or unequal. Unit tests are conventionally placed in the same file as the code under test, inside a submodule annotated #[cfg(test)] named mod tests, which brings the parent module's items into scope with use super::*; the #[cfg(test)] attribute tells the compiler to only compile that module when running tests, keeping test code out of release builds. Integration tests instead live in files under a top-level tests/ directory; each file there is compiled as its own separate crate that only sees the library's public API, so they test the crate the way an external consumer would. The #[should_panic] attribute marks a test as passing only if the annotated function panics, which is useful for verifying error conditions.
Cricket analogy: assert_eq! is like a third umpire's DRS review comparing the ball-tracking prediction to the actual impact and flashing a detailed replay if they disagree; #[cfg(test)] mod tests is like a practice-only net kept off the main stadium pitch on match day.
Example
// 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);
}
#[test]
#[should_panic]
fn wrong_result_panics() {
assert_eq!(add(2, 2), 5, "math is broken");
}
}Output
$ cargo test
running 2 tests
test tests::adds_two_numbers ... ok
test tests::wrong_result_panics ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered outKey Takeaways
- The #[test] attribute marks a function as a test case, run via cargo test.
- assert!, assert_eq!, and assert_ne! are the core assertion macros for verifying behavior.
- Unit tests typically live in a #[cfg(test)] mod tests submodule alongside the code they test.
- Integration tests live in a top-level tests/ directory and exercise only the crate's public API.
- #[should_panic] marks a test as passing only when the function panics, useful for testing error paths.
Practice what you learned
1. Which attribute marks a function as a test case in Rust?
2. What command runs all tests in a Rust project?
3. Where do unit tests conventionally live?
4. What does the #[should_panic] attribute do?
5. What distinguishes integration tests from unit tests in Rust?
Was this page helpful?
You May Also Like
Cargo and Crates in Rust
Understand Cargo, Rust's build system and package manager, and how crates and Cargo.toml/Cargo.lock manage dependencies.
Error Handling in Rust (Result)
Learn how Rust models recoverable errors with the Result enum and the ? operator instead of exceptions.
panic! and Unwinding in Rust
Learn how panic! signals unrecoverable errors in Rust, how stack unwinding works, and when to use it versus Result.
Functions in Rust
Learn how to declare, call, and return values from functions using Rust's fn syntax and expression-based return rules.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics