---
title: "14.x Chapter 14 summary and quiz"
description: "Review of writing and organizing tests, integration and doc tests, and running tests, with a chapter quiz and a test-writing exercise."
url: "https://learnrust.net/chapter-14/chapter-14-summary-and-quiz/"
last_updated: "2026-06-13"
---

# 14.x Chapter 14 summary and quiz

Rust ships a test framework, so this chapter taught testing as a habit. Review, then a quiz that has you write a small suite.

## Quick review

Tests buy you the confidence to change code: the compiler proves type and memory safety, but only a test can prove your logic is *correct* ([14.1](https://learnrust.net/chapter-14/introduction-to-testing/)). A good test is focused, covers edge cases, is independent of other tests, and is fast; it follows arrange-act-assert, and the assert is non-negotiable. A test is a function marked `#[test]` that passes if it runs without panicking ([14.2](https://learnrust.net/chapter-14/writing-tests/)). The assertion macros are `assert!`, `assert_eq!`, and `assert_ne!` (prefer the equality ones, which print both values on failure); `#[should_panic]` tests that code panics as intended; and a test can return `Result` so you can use `?` inside it.

Unit tests live in a `#[cfg(test)] mod tests` block in the same file as the code ([14.3](https://learnrust.net/chapter-14/organizing-unit-tests/)). `#[cfg(test)]` compiles them only for `cargo test`, so they cost nothing in release builds, and `use super::*;` pulls the code under test into scope. Because the test module is a child of what it tests, it *can* reach private functions, though the usual advice is to test mostly through the public API and reach into privates sparingly. Integration tests live in the `tests/` directory, where each file is a separate crate that sees only the public API ([14.4](https://learnrust.net/chapter-14/integration-tests/)), exercising the library as a real user would, which is exactly why logic belongs in `lib.rs` (chapter 13's IOU paid). Doc tests are the examples in `///` comments, compiled and run by `cargo test` so documentation can't go stale ([14.5](https://learnrust.net/chapter-14/doc-tests/)); write them as clear examples first, hide setup with `#`, and leave exhaustive cases to unit tests.

Running tests ([14.6](https://learnrust.net/chapter-14/running-tests/)): a string argument filters by substring (`cargo test adds`), `-- --show-output` reveals prints from passing tests (the `--` divides Cargo's args from the runner's), `#[ignore]` skips slow tests by default (`-- --ignored` runs only them), and tests run in parallel, so they must be independent (`-- --test-threads=1` forces sequential as a diagnostic).

## Quiz time

**Question #1**

Where do unit tests, integration tests, and doc tests each live, and which of them can see private functions?

<details class="solution">
<summary>Show solution</summary>

Unit tests: a `#[cfg(test)] mod tests` block in the same source file, and they *can* see private functions (child module of the code). Integration tests: the `tests/` directory, each file a separate crate, seeing only the public API. Doc tests: inside `///` comments on items, run as external-user code, seeing only the public API. Only unit tests can reach privates.

</details>

**Question #2**

This test is meant to verify that `withdraw` panics on an overdraft, but it passes for the wrong reasons sometimes. Improve it.

```rust
#[test]
#[should_panic]
fn overdraft_panics() {
    let mut acct = Account::new();
    acct.withdraw(100);   // empty account
}
```

<details class="solution">
<summary>Show solution</summary>

Add an expected message so the test only passes for the *right* panic:

```rust
#[test]
#[should_panic(expected = "insufficient funds")]
fn overdraft_panics() {
    let mut acct = Account::new();
    acct.withdraw(100);
}
```

With bare `#[should_panic]`, any panic passes the test, including an unrelated bug (an index error, an overflow). `expected = "insufficient funds"` requires the panic message to contain that text, confirming it's the overdraft panic.

</details>

**Question #3**

Why must tests be independent, and what does `cargo test -- --test-threads=1` help diagnose?

<details class="solution">
<summary>Show solution</summary>

Because `cargo test` runs tests in parallel by default, so two tests that share state (a file, a global) can interfere and fail unpredictably when run together. `--test-threads=1` runs them sequentially; if a test fails in the full parallel run but passes with one thread, shared state plus parallelism is the cause. The real fix is to make the tests independent, not to leave threads at 1.

</details>

**Question #4**

The exercise. Given a `pub fn is_palindrome(text: &str) -> bool` that ignores case and spaces (so "Race car" is a palindrome), write a `#[cfg(test)] mod tests` block with at least four focused tests covering: a simple palindrome, a non-palindrome, the case-and-space handling, and an edge case (empty string). Assume `is_palindrome("")` returns `true`.

<details class="solution">
<summary>Show solution</summary>

```rust
pub fn is_palindrome(text: &str) -> bool {
    let cleaned: String = text
        .chars()
        .filter(|c| !c.is_whitespace())
        .map(|c| c.to_ascii_lowercase())
        .collect();
    let reversed: String = cleaned.chars().rev().collect();
    cleaned == reversed
}

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

    #[test]
    fn simple_palindrome() {
        assert!(is_palindrome("level"));
    }

    #[test]
    fn not_a_palindrome() {
        assert!(!is_palindrome("rust"));
    }

    #[test]
    fn ignores_case_and_spaces() {
        assert!(is_palindrome("Race car"));
    }

    #[test]
    fn empty_string_is_palindrome() {
        assert!(is_palindrome(""));
    }
}
```

Each test is focused (one behavior), they're independent, and together they cover the easy path, the negative case, the case/space rule, and the empty-string edge case. The implementation uses iterator adapters (`filter`, `map`, `rev`, `collect`) that chapter 19 covers in full; for the test exercise, what matters is the four `assert!`/`assert!(!...)` checks. Running `cargo test` reports all four as `tests::...` passing.

</details>

Your code is now organized (chapter 13) and trustworthy (this chapter). Chapter 15 returns to the type system to remove a different kind of repetition: writing the same function over and over for different types. **Generics** let one definition serve many types, and the limitation they run into ("but I can't compare two values of an unknown type yet") is the exact cliffhanger that opens the traits chapter.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
