Welcome to this lesson on testing in Rust. Testing is an essential practice that verifies code correctness, prevents regressions, and helps maintain a reliable codebase as your project grows. Rust’s testing tools—integrated with Cargo—make it straightforward to write unit tests, verify panics, and use concise error handling in tests.Documentation Index
Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
Use this file to discover all available pages before exploring further.

- Creating a library and the built-in test template
- Writing and running unit tests with Cargo
- Using assertion macros and interpreting test output
- Handling panics and returning Result in tests
- Best practices for reliable unit tests

Creating a library and the built-in test template
When you create a new Rust library crate, Cargo often includes a small example test module inlib.rs. This template demonstrates the typical structure: a public function and a test module guarded by #[cfg(test)].
Example lib.rs:
Key points:
#[cfg(test)]makes the test module compile only when runningcargo test.mod testsis a conventional place to group unit tests.use super::*;imports parent-module items to make them available to the tests.#[test]marks functions that Cargo will execute as tests.
Running tests with Cargo
Run all tests with:cargo test— compiles the crate in test mode and runs all#[test]functions.cargo test --lib— run only library tests.cargo test <test-name>— run tests matching a substring.
A simple example: multiply
A small function with a unit test demonstrates the workflow:cargo test will compile in test mode and execute test_multiply. The test harness prints a per-test pass/fail line and a summary.
![A four-step flowchart titled "Understanding Test Output" showing Compile Code → Run Tests → Test Results → Compile Code. Each step notes that Rust/Cargo compiles in test mode, runs functions marked #[test], shows pass/fail status for each test, and displays a summary of passed/failed tests.](https://mintcdn.com/kodekloud-c4ac6d9a/qTPiNmqXKGQjbUh5/images/Rust-Programming/Testing-Continuous-Integration/Introduction-to-Testing-in-Rust/rust-cargo-test-flowchart.jpg?fit=max&auto=format&n=qTPiNmqXKGQjbUh5&q=85&s=a89ef8ed302532c031a2546aec4f6787)
Demonstrating a failing test
When an assertion fails, Cargo reports a failure with useful context: the test name, a panic message, and the expected vs actual values when available. Intentional failing example:Common assertion macros
Use these assertion macros inside tests to express expectations clearly.| Macro | Description | Example |
|---|---|---|
assert!(cond) | Asserts a boolean condition is true | assert!(2 + 2 == 4); |
assert_eq!(left, right) | Asserts two expressions are equal (prints values on failure) | assert_eq!(multiply(2, 3), 6); |
assert_ne!(left, right) | Asserts two expressions are not equal | assert_ne!(multiply(2, 3), 7); |
assert!(cond, "msg") | Asserts with a custom failure message | assert!(x > 0, "x must be positive"); |
Testing for panics with #[should_panic]
To test that code panics in error conditions, use#[should_panic]. Optionally provide expected = "text" to match the panic message.
Example: a divide function that panics on division by zero
Use
#[should_panic(expected = "...")] carefully: matching an expected substring can make tests fragile if panic messages change. Prefer asserting error types or Result-based APIs when possible.Writing tests that return Result<T, E>
Instead of using panics, test functions may returnResult<(), E>. This lets you use the ? operator for concise error handling—tests that return Ok(()) pass; returning Err(_) fails.
![A presentation slide titled “Writing Tests With Result<T, E]” with a colored banner that reads “Using Result<T, E> in tests enables concise error handling with the ? operator.” The slide has a © Copyright KodeKloud notice in the bottom-left.](/images/Rust-Programming/Testing-Continuous-Integration/Introduction-to-Testing-in-Rust/writing-tests-result-t-e-slide.jpg)
Cargo.toml exists in the test working directory, this test will pass. Returning Result is especially useful when tests perform I/O or use other fallible APIs.
Interpreting test output
When running tests, Cargo prints:- a per-test line showing name and status (ok/FAILED/ignored)
- detailed failure reports including backtraces (if
RUST_BACKTRACE=1) - a final summary with counts of passed/failed/ignored tests
Best practices

- Keep tests independent: avoid shared mutable state and order-dependent behavior.
- Use descriptive names: a clear test name documents intent and simplifies debugging.
- Test edge cases: include boundary conditions, error paths, and invalid inputs.
- Prefer explicit checks over fragile string matching for panics—use
Result-based APIs or error types when possible. - Refactor tests alongside code: remove duplication and keep tests readable and maintainable.
Quick reference
| Topic | Command / Pattern |
|---|---|
| Run all tests | cargo test |
| Run specific test | cargo test <name> |
| Test module guard | #[cfg(test)] |
| Mark test | #[test] |
| Expect panic | #[should_panic] or #[should_panic(expected = "...")] |
| Result-returning test | fn test() -> Result<(), E> |