Skip to content

Commit 6bf69bd

Browse files
πŸ“– [Docs]: Module test layouts aligned with Process-PSModule (#88)
Module authors can now choose a test layout that matches their module's size while preserving behavior coverage for every public command. ## Changed: Test suites can follow Simple, Standard, or Advanced layouts Simple modules use one root suite, Standard modules group root suites by public function domain, and Advanced modules use recursive directories with Process-PSModule's configuration, container, and test-file precedence. Grouped suites are explicitly supported without weakening the requirement to test every public command's behavior. --- <details> <summary>Technical details</summary> - Documents live Process-PSModule directory discovery and first-dot `TestName` derivation. - Aligns Pester 6 requirements, locally runnable tests, and module-owned mocking boundaries with MSX guidance. - Clarifies root-only `BeforeAll.ps1` and `AfterAll.ps1` workflow phases. - Validation passed: Zensical build, Super-Linter, CodeQL, and diff checks. </details> <details> <summary>Relevant issues (or links)</summary> - Fixes #87 </details> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 993ae01 commit 6bf69bd

1 file changed

Lines changed: 34 additions & 9 deletions

File tree

β€Žsrc/docs/Modules/Standards.mdβ€Ž

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Keep related things together so the connection between code and its context is v
107107

108108
- `#Requires -Modules` belongs in the function files that use the dependency, not in a central manifest.
109109
- Parameter descriptions are `#` comments directly above each parameter in `param()`, not `.PARAMETER` blocks.
110-
- Tests for a public function live alongside it, not in a separate unrelated folder.
110+
- Tests follow the same domain boundaries as public source: cover every public command under `tests/`, and align grouped suites with `src/functions/public/<Group>/`.
111111

112112
### Linear versioning
113113

@@ -287,17 +287,42 @@ On abandoned (closed without merge) PRs, the pipeline cleans up any prerelease t
287287

288288
## Tests
289289

290-
- Pester tests under `tests/`.
291-
- Filenames: `<ModuleName>.Tests.ps1` or `<Function>.Tests.ps1`.
292-
- One test file per public command for unit tests; integration tests grouped by scenario.
293-
- Tests run against the built module artifact installed locally, across a multi-OS matrix (Linux, macOS, Windows).
290+
Keep Pester tests under `tests/`. Every public command's behavior must be covered, but commands do not require separate test files. The framework's `FunctionTest` check confirms that every public command is referenced somewhere under `tests/`, so a grouped suite satisfies the structural check; the suite must still assert the command's behavior.
291+
292+
The profiles below are repository layout conventions, not Process-PSModule configuration modes. Use the simplest profile that keeps the suite readable:
293+
294+
| Profile | Test layout |
295+
| ------- | ----------- |
296+
| **Simple** | One root `tests/<ModuleName>.Tests.ps1` file covers the module. |
297+
| **Standard** | One root `tests/<Group>.Tests.ps1` file per public function group. Ungrouped commands and cross-cutting scenarios may remain in separate root `*.Tests.ps1` files. |
298+
| **Advanced** | Organize tests in recursive subdirectories. Process-PSModule discovers each directory independently using the precedence below. |
299+
300+
For the Advanced profile, Process-PSModule traverses `tests/` and every subdirectory. Within each directory:
301+
302+
1. Exactly one `*.Configuration.ps1` file is the only discovered test entry. More than one configuration file in the same directory is an error.
303+
2. Otherwise, one or more `*.Container.ps1` files are all discovered, and `*.Tests.ps1` files in that directory are ignored.
304+
3. Otherwise, all `*.Tests.ps1` files in that directory are discovered.
305+
306+
Process-PSModule derives `TestName` from the file's basename before the first dot. For example, `Projects.Unit.Tests.ps1` produces `Projects`. Use a unique first-dot prefix for every discovered entry so matrix jobs and result artifacts are unambiguous.
307+
308+
Each `*.Tests.ps1` file must declare the Pester 6 requirement:
309+
310+
```powershell
311+
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.0.0'; MaximumVersion = '6.*' }
312+
```
313+
314+
Tests run against the built module artifact installed locally, across a multi-OS matrix (Linux, macOS, Windows). The full suite must also remain runnable locally without mandatory cloud resources, special access, or secrets that cannot be mocked.
315+
316+
Unit tests mock boundaries owned by the module, not third-party APIs or SDKs directly. Wrap external dependencies behind a thin module-owned interface and fake that interface. Where the external contract matters, back the unit tests with a small integration suite that exercises the live dependency.
294317

295318
### Shared test infrastructure
296319

297-
Tests run in parallel across multiple OS runners. Provision shared infrastructure once rather than inside each test file:
320+
Tests run in parallel across multiple OS runners. When integration tests need shared infrastructure, provision it once rather than inside each test file:
321+
322+
- `tests/BeforeAll.ps1` β€” special root workflow phase that runs once before the full test matrix. Create shared resources here.
323+
- `tests/AfterAll.ps1` β€” special root workflow phase that runs once after the full test matrix. Remove shared resources here.
298324

299-
- `tests/BeforeAll.ps1` β€” runs once before the full test matrix. Create shared resources here.
300-
- `tests/AfterAll.ps1` β€” runs once after the full test matrix. Remove shared resources here.
325+
These exact root files are detected separately and are not recursive test entries. Nested files with these names do not create workflow phases.
301326

302327
Use `$env:GITHUB_RUN_ID` (stable per workflow run, shared across all runners) for deterministic resource names:
303328

@@ -321,7 +346,7 @@ The CI pipeline automatically tests every source file against the following rule
321346
| `FunctionName` | Filename must match the function or filter name |
322347
| `CmdletBinding` | Every function must have `[CmdletBinding()]` |
323348
| `ParamBlock` | Every function must have a `param()` block |
324-
| `FunctionTest` | Every public function must have a corresponding test |
349+
| `FunctionTest` | Every public function must be referenced by the tests; its behavior must be covered whether the suite is per-command or grouped |
325350

326351
To skip a specific rule for one file only, add a comment at the very top of that file:
327352

0 commit comments

Comments
Β (0)