Skip to content

Expert · Testing, tooling and unsafe Rust · topic 40 of 50 · 25 min

Integration tests and doc tests

Explanation

Integration tests live in the tests/ folder; each file is a separate crate that can only use your library's public API, just like a real user. Doc tests are code examples inside /// documentation comments, and cargo test runs them too, so your examples never go out of date.

Two more kinds of test

Unit tests check small pieces from the inside. But you also need to know that the pieces work together, through the public interface other people actually call. And if you write examples in your documentation, you want them to stay correct. Rust handles both with built-in tools.

Integration tests: the outside view

output
my_shop/
├── Cargo.toml
├── src/
│   └── lib.rs          // pub fn cart_total(...), pub struct Cart ...
└── tests/
    ├── checkout.rs     // integration tests
    └── common/
        └── mod.rs      // shared helpers (not a test file itself)
Integration tests need a library crate (src/lib.rs) to import.
rust
// tests/checkout.rs
use my_shop::Cart;

#[test]
fn full_checkout_with_coupon() {
    let mut cart = Cart::new();
    cart.add("kurta", 1200);
    cart.add("dupatta", 400);
    cart.apply_coupon("FESTIVE10");
    assert_eq!(cart.total(), 1440);
}
No #[cfg(test)] needed: files in tests/ are compiled only for cargo test.

Each file in tests/ is compiled as its own separate crate. It imports your library by its package name, and it can only see items marked pub, exactly like an outside user. If something is awkward to test from here, it is probably awkward to use too, which is useful feedback on your API design.

Helpers shared by several test files go in tests/common/mod.rs and are brought in with mod common;. Using the folder form stops cargo from treating the helper file as a test file of its own.

bash
cargo test --test checkout   # only tests/checkout.rs
cargo test --lib             # only unit tests in the library
Run one kind of test at a time.

Doc comments and doc tests

Comments starting with /// are documentation comments. They are written in Markdown and describe the item below them. cargo doc turns them into an HTML website like the one on docs.rs. Any code block inside them is compiled and run as a test by cargo test.

rust
/// Adds GST to an amount in whole rupees.
///
/// # Examples
///
/// ```
/// let total = my_shop::with_gst(1000, 18);
/// assert_eq!(total, 1180);
/// ```
pub fn with_gst(amount: u32, rate: u32) -> u32 {
    amount + amount * rate / 100
}
If someone changes with_gst and the example becomes wrong, cargo test fails.
bash
cargo test --doc   # run only the doc tests
cargo doc --open   # build the docs and open them in the browser
Doc tests use the public API, like integration tests.
Tip: Common sections in doc comments are # Examples, # Errors (when the function returns Err), # Panics (when it can panic) and # Safety (for unsafe functions). Libraries on crates.io are judged a lot by their docs.

How the three fit together

KindLives inSeesChecks
Unit testsrc/*.rs, in mod testsPrivate and public itemsSmall pieces of logic
Integration testtests/*.rsPublic API onlyParts working together, as a user
Doc test/// commentsPublic API onlyExamples in the docs stay correct

Common mistakes

  • Trying to write integration tests for a binary-only crate (just main.rs). Move the logic into src/lib.rs and keep main.rs thin.
  • Putting helpers in tests/common.rs, which cargo runs as an empty test file. Use tests/common/mod.rs.
  • Doc examples that only compile on your machine (absolute paths, network). Keep examples self-contained.
  • Forgetting the crate name in doc examples: inside docs you must write my_shop::with_gst, as an outside user would.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Integration tests and doc tests · 1/6

tests/checkout.rs

Integration tests live in the tests folder. Each file is its own crate, so it can only use your public API.

Prefer a video by a teacher? Find free videos on “rust integration tests doc tests cargo doc” on YouTube ↗

In real life: A public library for Indian number formatting

A developer publishes a crate that formats numbers in the Indian system (12,34,567 instead of 1,234,567). Unit tests check the grouping logic inside. Integration tests call the public format_inr function exactly as users will. Doc tests in the README-style examples make sure every example shown on docs.rs really prints what it claims.

rust
/// Formats a whole number with Indian digit grouping.
///
/// ```
/// assert_eq!(inr::group(1234567), "12,34,567");
/// assert_eq!(inr::group(999), "999");
/// ```
pub fn group(n: u64) -> String {
    let s = n.to_string();
    if s.len() <= 3 {
        return s;
    }
    let (rest, last3) = s.split_at(s.len() - 3);
    let mut parts: Vec<&str> = Vec::new();
    let mut i = rest.len();
    while i > 2 {
        parts.push(&rest[i - 2..i]);
        i -= 2;
    }
    parts.push(&rest[..i]);
    parts.reverse();
    format!("{},{}", parts.join(","), last3)
}
The doc example is both documentation and a test.

Practice (0/3 done)

Task 1 · Question

What can an integration test in tests/ access?

Task 2 · Question

Where do you put helper functions shared by several integration test files?

Task 3 · Practice

Take a project with logic in src/lib.rs (or move it there). Add /// docs with a runnable example to two public functions, add tests/api.rs with three integration tests, and run cargo test, cargo test --doc and cargo doc --open.

Quick quiz

Question 1

1.Which comment style creates documentation for the item below it?

Question 2

2.What happens to code blocks inside /// comments when you run cargo test?

Question 3

3.Why might a binary-only crate be hard to integration test?

Question 4

4.Which command builds HTML documentation?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which comment style creates documentation for the item below it?

Answer: /// comment

/// is an outer doc comment.

What happens to code blocks inside /// comments when you run cargo test?

Answer: They are compiled and run as tests

Doc tests keep examples correct.

Why might a binary-only crate be hard to integration test?

Answer: Integration tests import a library crate; put the logic in lib.rs

Keep main.rs thin and put logic in the library.

Which command builds HTML documentation?

Answer: cargo doc

cargo doc builds docs; --open opens them.

Why commit Cargo.lock for an application?Related

Answer: It pins exact dependency versions so builds are repeatable

Cargo.lock records the exact versions used.

From Packages, crates and modules

What does an integration test check that a unit test does not?Related

Answer: That several parts, like handler and database, work together

Integration tests check the joins between parts, where many real bugs live.

From Integration testing: checking parts work together

What does cargo-audit check?Related

Answer: Dependencies against a database of known vulnerabilities

It uses the RustSec advisory database.

From Tooling: rustfmt, Clippy, docs and editions

Where does a cargo project keep its main code?Related

Answer: src/main.rs

cargo new puts the starting code in src/main.rs.

From Your first program and cargo

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Testing, tooling and unsafe Rust” before its exam: Tooling: rustfmt, Clippy, docs and editions, Unsafe Rust: what it is and is not.
  2. Module 10 exam: Testing, tooling and unsafe Rust · 12 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Real projects: CLI, web and WebAssembly. 50 topics left in the course before the final exam.

← Previous: Unit tests