Skip to content

Commit f57331a

Browse files
committed
docs(dev/guide): document the external-file unit-test module convention
1 parent 345633a commit f57331a

5 files changed

Lines changed: 28 additions & 0 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
1717
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
18+
- A unit-test module may sit inline as `mod tests { ... }` when short, but once it grows long enough to noticeably extend the parent, move it to an external `src/foo/tests.rs` (or `src/foo/bar/tests.rs` for `src/foo/bar.rs`) and declare it with `#[cfg(test)] mod tests;` at the end of the parent — keep this layout even when the parent has no other submodules
1819
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1920
- If you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, help text files, and `USAGE.md`. **Do not attempt to regenerate these files manually** — always use the script.
2021
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. If a sync test fails, read its error message carefully and run the exact command it tells you to run.

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
1717
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
18+
- A unit-test module may sit inline as `mod tests { ... }` when short, but once it grows long enough to noticeably extend the parent, move it to an external `src/foo/tests.rs` (or `src/foo/bar/tests.rs` for `src/foo/bar.rs`) and declare it with `#[cfg(test)] mod tests;` at the end of the parent — keep this layout even when the parent has no other submodules
1819
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1920
- If you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, help text files, and `USAGE.md`. **Do not attempt to regenerate these files manually** — always use the script.
2021
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. If a sync test fails, read its error message carefully and run the exact command it tells you to run.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
1717
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
18+
- A unit-test module may sit inline as `mod tests { ... }` when short, but once it grows long enough to noticeably extend the parent, move it to an external `src/foo/tests.rs` (or `src/foo/bar/tests.rs` for `src/foo/bar.rs`) and declare it with `#[cfg(test)] mod tests;` at the end of the parent — keep this layout even when the parent has no other submodules
1819
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1920
- If you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, help text files, and `USAGE.md`. **Do not attempt to regenerate these files manually** — always use the script.
2021
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. If a sync test fails, read its error message carefully and run the exact command it tells you to run.

CONTRIBUTING.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,30 @@ ExitCode::from(match self {
342342
})
343343
```
344344

345+
## Unit Tests
346+
347+
A unit-test module may either sit inline as `mod tests { ... }` in its parent or live in a dedicated external `tests` submodule. The inline form is appropriate for short test modules; once the block grows long enough to noticeably extend the length of the parent and get in the way of reading the rest of the module, move the tests into an external file.
348+
349+
### When the inline form is acceptable
350+
351+
There is nothing wrong with `mod tests { ... }` by itself. Reserve the inline form for modules whose entire test suite fits in a small number of lines, so that the block does not noticeably extend the length of the parent. The problem that this convention addresses is a long inline block that extends the length of the main module and makes it harder to traverse. Use the number of lines as the deciding factor: once the inline tests grow long enough to get in the way of reading the rest of the module, move them into an external file.
352+
353+
### Where the external file sits
354+
355+
When the tests live externally, the parent declares them at the end of the file with the standard declaration:
356+
357+
```rust
358+
#[cfg(test)]
359+
mod tests;
360+
```
361+
362+
The external file itself sits in a directory named after the parent, using the same path regardless of whether the parent has any other submodules. Concretely:
363+
364+
- For `src/foo.rs`, the tests file is `src/foo/tests.rs`.
365+
- For `src/foo/bar.rs`, the tests file is `src/foo/bar/tests.rs`.
366+
367+
Do not flatten the tests into a sibling file such as `src/foo_tests.rs`, and do not skip the intermediate directory when the parent currently has no other submodules. This mirrors the flat file pattern (`module.rs` rather than `module/mod.rs`) described under [Module Organization](#module-organization).
368+
345369
## Setup
346370

347371
Install the required Rust toolchain and components before running any checks:

template/ai-instructions/shared.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c
1515
- Error types: only derive `Display` and `Error` from `derive_more` when each is actually needed — not all displayable types are errors
1616
- Minimize `unwrap()` in non-test code — use proper error handling
1717
- Prefer `#[cfg_attr(..., ignore = "reason")]` over `#[cfg(...)]` to skip tests — use `#[cfg]` on tests only when the code cannot compile under the condition (e.g., references types/functions that don't exist on other platforms)
18+
- A unit-test module may sit inline as `mod tests { ... }` when short, but once it grows long enough to noticeably extend the parent, move it to an external `src/foo/tests.rs` (or `src/foo/bar/tests.rs` for `src/foo/bar.rs`) and declare it with `#[cfg(test)] mod tests;` at the end of the parent — keep this layout even when the parent has no other submodules
1819
- Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy`
1920
- If you change CLI arguments, help text, or anything that affects command-line output, run `./generate-completions.sh` to regenerate the shell completion files, help text files, and `USAGE.md`. **Do not attempt to regenerate these files manually** — always use the script.
2021
- Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes. If a test fails with a hint about `TEST_SKIP`, follow the hint and rerun with the suggested variable. If a sync test fails, read its error message carefully and run the exact command it tells you to run.

0 commit comments

Comments
 (0)