tezvyn:

Rust Unit Tests: Co-locating Tests with Code

AI-drafted, machine-checkedSource: doc.rust-lang.orgbeginner

In Rust, unit tests live inside a special `tests` module within the same file as the code they're testing. This lets you test a module in isolation, including its private functions.

WHY IT EXISTS Rust provides a clear testing organization to help developers quickly pinpoint bugs. Unit tests exist to test individual pieces of code in isolation from the rest of the system. This separation from integration tests, which check how different parts work together, makes debugging faster and more focused.

THE MENTAL MODEL Think of a Rust unit test module as a "test-only" companion living inside the same file as your implementation. It's conditionally compiled, so it only exists when you run cargo test. This co-location gives it special access to test private, internal logic that isn't part of the public API, like a mechanic using special tools inside the engine bay that a driver never sees.

HOW IT WORKS In the same file as the code you want to test, you create a child module named tests. You must annotate this module with #[cfg(test)]. This attribute tells Rust to compile and run the module's code only when you execute cargo test, not cargo build. Inside the tests module, you bring the parent module's items into scope with use super::*. This allows your test functions, marked with #[test], to call any function in the parent module, including private ones.

WHEN TO USE IT Use this pattern to test the logic of a single module in isolation. It's perfect for verifying specific algorithms, edge cases in private helper functions, and ensuring individual components behave as expected before they are integrated. This is your go-to for fine-grained, focused testing.

WHEN NOT TO USE IT Avoid this for tests that span multiple modules or need to simulate how an external user would interact with your library. For those scenarios, use integration tests. Integration tests reside in a separate tests/ directory at the project root and can only access your library's public API, mimicking real-world usage.

ONE CANONICAL EXAMPLE To test a private function like internal_adder(a, b), you would place a tests module in the same file. A test function inside, like it_adds_internally(), can call internal_adder directly because use super::* brings the parent's items into scope. The entire tests module is wrapped in #[cfg(test)], so it's excluded from a production cargo build, allowing you to thoroughly test implementation details without exposing them or bloating the binary.

Read the original → doc.rust-lang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.