You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
📖 [Docs]: PowerShell module test profiles now match recursive discovery (#98)
PowerShell module authors can now choose a documented Simple, Standard,
or Advanced test profile, with Process-PSModule discovery and trigger
behavior described consistently across the authoring and framework
guidance.
## New: Three module test profiles
Simple keeps one root-level `./tests/<ModuleName>.Tests.ps1` suite.
Standard keeps one root-level `./tests/<Group>.Tests.ps1` suite per
public function group, while allowing separately named root-level suites
for ungrouped functions and cross-cutting behavior. Advanced is
recommended when independent configurations, containers, or ordinary
test files should be organized in recursively discovered subdirectories.
These are documentation conventions over one filesystem discovery
engine, not selectable modes.
## Changed: Framework discovery and triggering are explicit
Process-PSModule applies precedence independently in every directory:
exactly one `*.Configuration.ps1` is selected first and suppresses
sibling containers and tests; more than one configuration errors.
Otherwise, one or more `*.Container.ps1` files are selected and suppress
sibling tests. Otherwise, all `*.Tests.ps1` files are selected. Layouts
may mix across directories because child directories are inspected
independently.
Every discovered artifact needs a unique prefix before its first dot
because that prefix becomes `TestName`. Only root `tests/BeforeAll.ps1`
and `tests/AfterAll.ps1` are special workflow phases.
`.github/PSModule.yml` exposes no profile, layout, or suite-matrix
selector; `Settings.Test.Module.Suites` is computed internally from
module-local files.
Recursive discovery is separate from important-file triggering. The
default `ImportantFilePatterns` match only `^src/` and `^README\.md$`,
so test-only changes do not enter the important-change build, test, and
publish path unless a repository adds `^tests/` and any relevant
settings or workflow paths while retaining the defaults it still needs.
---
<details>
<summary>Technical details</summary>
- Adds the general PowerShell test profile guidance under
`src/docs/Coding-Standards/PowerShell/Testing.md` and links it from the
generated index and site navigation.
- Keeps exact recursive discovery precedence, sibling suppression,
unique naming, and root-only phase mechanics in Process-PSModule
framework documentation.
- Aligns Process-PSModule repository structure, pipeline stages, usage,
and configuration pages with recursive module-local discovery.
- Marks internal `*.Suites` matrices as computed workflow outputs rather
than authorable settings.
- Documents that test-only changes require `^tests/` in
`ImportantFilePatterns` to trigger the important-change path and shows
settings/workflow path examples.
</details>
<details>
<summary>Relevant issues (or links)</summary>
- No linked issue — implementation was explicitly requested by the
release integration session.
</details>
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
description: Pester test naming and the Simple, Standard, and Advanced profiles for PowerShell modules.
4
+
---
5
+
6
+
# PowerShell Testing
7
+
8
+
PowerShell tests build on the [testing baseline](../Testing.md): they are test-first, locally runnable, deterministic, isolated, and automated in CI. Use [Pester](https://pester.dev/) and name ordinary test files `*.Tests.ps1`.
9
+
10
+
## Module test profiles
11
+
12
+
Simple, Standard, and Advanced are documentation profiles: conventions for arranging module-local tests, not selectable Process-PSModule modes. All three feed the same filesystem discovery engine; there is no layout setting in `.github/PSModule.yml`.
13
+
14
+
Choose the smallest profile that keeps the suite easy to navigate.
15
+
16
+
### Simple
17
+
18
+
Use one root-level test file named after the module:
19
+
20
+
```text
21
+
tests/
22
+
└── <ModuleName>.Tests.ps1
23
+
```
24
+
25
+
The Simple layout fits a module whose tests remain readable as one suite.
26
+
27
+
### Standard
28
+
29
+
Use one root-level test file per public function group:
30
+
31
+
```text
32
+
tests/
33
+
├── <GroupOne>.Tests.ps1
34
+
├── <GroupTwo>.Tests.ps1
35
+
└── <Behavior>.Tests.ps1 (optional)
36
+
```
37
+
38
+
`<Group>` names the public function group the file covers. When public functions are organized under `src/functions/public/<Group>/`, use the same group name for the test file.
39
+
40
+
Ungrouped public functions and cross-cutting module behavior may use separate root-level `*.Tests.ps1` suites where appropriate. Name each suite after the functions or behavior it covers.
41
+
42
+
### Advanced
43
+
44
+
The Advanced profile is recommended when parts of the suite need independent Pester configurations, containers, or ordinary test files. Organize those parts in subdirectories:
45
+
46
+
```text
47
+
tests/
48
+
├── Unit/ # No configuration or containers: all test files
49
+
│ ├── GroupOne.Tests.ps1
50
+
│ └── GroupTwo.Tests.ps1
51
+
├── Integration/
52
+
│ └── Integration.Configuration.ps1
53
+
└── Compatibility/
54
+
├── Linux.Container.ps1
55
+
└── Windows.Container.ps1
56
+
```
57
+
58
+
Different directories may use different forms. Process-PSModule recursively applies its [per-directory discovery precedence](../../Frameworks/Process-PSModule/pipeline-stages.md#module-local-test-discovery), including sibling suppression, unique test-name requirements, and root-only workflow phases.
Copy file name to clipboardExpand all lines: src/docs/Coding-Standards/PowerShell/index.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,6 +16,7 @@ This standard builds on the [language-agnostic baseline](../index.md); where the
16
16
|[Functions](Functions.md)| Advanced functions — CmdletBinding, typed and validated parameters, pipeline blocks, ShouldProcess, and required comment-based help. |
17
17
|[Classes](Classes.md)| When to reach for a PowerShell class, and how to structure its members, constructors, and documentation. |
18
18
|[Scripts](Scripts.md)| Structure for standalone .ps1 scripts — requirements, parameters, help, and keeping the script thin. |
19
+
|[PowerShell Testing](Testing.md)| Pester test naming and the Simple, Standard, and Advanced profiles for PowerShell modules. |
19
20
|[Messaging](Messaging.md)| Write-Verbose for user-facing operational progress and normal troubleshooting; Write-Debug for developer-focused internals and deep diagnostics. |
20
21
|[Version Constraints](Version-Constraints.md)| Express module and package version constraints as NuGet version ranges — the canonical notation across PSResourceGet, .NET package references, and (mapped) #Requires and module manifests. |
21
22
|[Module Requirements](Requires-Modules.md)| Valid `#Requires -Modules` version specifications — minimum, major-lock (with the `N.*` wildcard), exact, and GUID identity pinning — with an executable proof. |
@@ -80,4 +81,4 @@ Beyond the basics, these language-specific habits keep PowerShell correct and fa
80
81
The toolchain enforces this standard in CI — it does not define it. The rules above are the source of truth; each tool's configuration is derived from them:
81
82
82
83
-**[PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer)** is the linter and formatter; its settings are derived from this standard, so passing it cleanly means matching the standard. Let it format — do not hand-format.
83
-
-**[Pester](https://pester.dev/)** is the test framework; test files are named `*.Tests.ps1`. See the [Testing baseline](../Testing.md).
84
+
-**[Pester](https://pester.dev/)** is the test framework; test files are named `*.Tests.ps1`. See [PowerShell Testing](Testing.md) for module test layouts.
- Imports and tests the module in parallel (matrix) using Pester tests from the module repository.
112
+
- Imports and tests the module in parallel (matrix) using module-local Pester tests.
113
+
- Discovers module-local tests recursively under `tests/`, applying the [per-directory precedence](#module-local-test-discovery) independently at every level.
113
114
- Module test files declare a Pester **6.x** requirement via `#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.0.0'; MaximumVersion = '6.*' }` — a convention module authors add to each `*.Tests.ps1`, not something this pipeline injects. The [Invoke-Pester](https://github.com/PSModule/Invoke-Pester) action installs a matching `6.x`, so minor and patch updates flow in automatically while a new major stays a deliberate, reviewed change.
114
115
- Supports setup and teardown scripts executed via separate dedicated jobs:
115
-
-`BeforeAll`: Runs once before all test matrix jobs to set up the test environment (e.g., deploy infrastructure, download test data).
116
-
-`AfterAll`: Runs once after all test matrix jobs complete to clean up the test environment (e.g., remove test resources, clean up databases).
117
-
- Setup/teardown scripts are automatically detected in test directories and executed with the same environment variables as the tests.
118
-
- This produces a JSON-based report that is used by [Get-PesterTestResults](#get-test-results) evaluate the results of the tests.
116
+
-`BeforeAll`: Runs root `tests/BeforeAll.ps1` once before all test matrix jobs to set up the test environment (e.g., deploy infrastructure, download test data).
117
+
-`AfterAll`: Runs root `tests/AfterAll.ps1` once after all test matrix jobs complete to clean up the test environment (e.g., remove test resources, clean up databases).
118
+
- Setup and teardown detection is not recursive; nested files with those names are not workflow phases.
119
+
- This produces a JSON-based report that is used by [Get-PesterTestResults](#get-test-results) to evaluate the results of the tests.
120
+
121
+
### Module-local test discovery
122
+
123
+
Simple, Standard, and Advanced are [documentation profiles](../../Coding-Standards/PowerShell/Testing.md#module-test-profiles), not selectable workflow modes. The same discovery engine handles every profile. `.github/PSModule.yml` has no test-layout or suite-matrix setting; `Settings.Test.Module.Suites` is computed internally from the repository files.
124
+
125
+
Process-PSModule inspects `tests/` recursively. Within each directory it uses the first matching form:
126
+
127
+
1. Exactly one `*.Configuration.ps1`. Discovery fails when a directory contains more than one. When selected, sibling `*.Container.ps1` and `*.Tests.ps1` files are not independently selected.
128
+
2. Otherwise, one or more `*.Container.ps1`. When selected, sibling `*.Tests.ps1` files are not independently selected.
129
+
3. Otherwise, all `*.Tests.ps1`.
130
+
131
+
The selected form takes precedence only in that directory. Child directories are still inspected independently, so a repository may mix configurations, containers, and ordinary test files across different directories.
132
+
133
+
Every discovered artifact needs a unique prefix before its first dot because that prefix becomes `TestName`. For example, `Users.Unit.Tests.ps1` and `Users.Integration.Tests.ps1` both become `Users`; use distinct prefixes such as `UsersUnit` and `UsersIntegration`.
119
134
120
135
### Setup and Teardown Scripts
121
136
122
137
The workflow supports automatic execution of setup and teardown scripts for module tests:
123
138
124
-
-Scripts are automatically detected and executed if present.
139
+
-The exact root paths `tests/BeforeAll.ps1` and `tests/AfterAll.ps1` are detected and executed if present.
125
140
- If no scripts are found, the workflow continues normally.
141
+
- Detection is not recursive.
126
142
127
143
#### Setup - `BeforeAll.ps1`
128
144
129
-
- Place in your test directories (`tests/BeforeAll.ps1`).
145
+
- Place at the root test path `tests/BeforeAll.ps1`.
130
146
- Runs once before all test matrix jobs to prepare the test environment.
131
147
- Deploy test infrastructure, download test data, initialize databases, or configure services.
132
148
- Has access to the same environment variables as your tests (secrets, GitHub token, etc.).
├── tests/ # Pester suites executed during validation
29
+
├── tests/ # Pester suites; the Simple layout is shown
30
30
│ ├── AfterAll.ps1 (optional) # Cleanup script for ModuleLocal runs
31
31
│ ├── BeforeAll.ps1 (optional) # Setup script for ModuleLocal runs
32
-
│ └── <ModuleName>.Tests.ps1 # Primary test entry point
32
+
│ └── <ModuleName>.Tests.ps1 # Simple: one root-level module suite
33
33
├── .gitattributes # Normalizes line endings across platforms
34
34
├── .gitignore # Excludes build artifacts from source control
35
35
├── LICENSE # License text surfaced in manifest metadata
36
36
└── README.md # Repository overview rendered on GitHub and docs landing
37
37
```
38
38
39
+
The tree shows the [Simple PowerShell test profile](../../Coding-Standards/PowerShell/Testing.md#simple), not an exclusive test-file shape. Standard keeps one root-level `tests/<Group>.Tests.ps1` file per public function group. Advanced uses recursively discovered subdirectories, and layouts may mix across directories. Process-PSModule defines the exact [per-directory precedence and sibling suppression](pipeline-stages.md#module-local-test-discovery).
40
+
41
+
These names describe repository conventions, not settings. `.github/PSModule.yml` does not select a test profile. The optional `tests/BeforeAll.ps1` and `tests/AfterAll.ps1` files are root-only workflow phases and are not discovered recursively.
42
+
39
43
Key expectations:
40
44
41
-
- Keep at least one exported function under `src/functions/public/` and corresponding tests in `tests/`.
45
+
- Keep at least one exported function under `src/functions/public/` and corresponding tests in `tests/` using a [documented test profile](../../Coding-Standards/PowerShell/Testing.md#module-test-profiles).
42
46
- Keep documentation site configuration in `.github/zensical.toml`.
43
47
- Optional folders (`assemblies`, `formats`, `types`, `variables`, and others) are processed automatically when present.
44
48
- Markdown files in `src/functions/public` subfolders become documentation pages alongside generated help.
0 commit comments