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
feat(#200): establish mirrored test layout convention
- Document the source-mirrored, dot-source-first test loading pattern in the testing-policy instruction and pester-testing skill (mirrored into the agentic-copilot scaffold), add a non-blocking scripts/build/Get-TestMirrorStatus.ps1 helper that reports src files without a mirrored test, add proof-of-concept mirrored tests for
- Format-ProjectJsonValue and Get-ProjectJsonValueTypeName, remove the dist-psm1 coverage Path override so project.json owns the coverage Path, and temporarily disable the Pester coverage gate while the existing suite migrates to the new pattern.
Use this file when changing production code, tests, coverage behavior, or CI test flows.
10
10
11
+
## Test loading pattern (mirrored, dot-source-first)
12
+
13
+
NovaModuleTools is migrating tests to a 1:1 source-to-test layout per issue #208. New and migrated tests follow this loading pattern:
14
+
15
+
- The test file's `BeforeAll` dot-sources **only** the `src/**/*.ps1` files it needs (the source under test plus its private collaborators that are not being mocked).
16
+
- Tests do **not**`Import-Module $project.OutputModuleDir`.
17
+
- Tests do **not** use `InModuleScope <ModuleName> { ... }`. Mocked functions live in the same scope as the test because they were dot-sourced into it.
18
+
- Shared fixtures and dot-source helpers live in `tests/TestHelpers/`.
19
+
- This makes `project.json``Pester.CodeCoverage.Path = ["src/public/*.ps1", "src/private/**/*.ps1"]` produce real source-file coverage and means `Test-NovaBuild` does not require a `dist/` folder.
Legacy tests that still use `Import-Module $project.OutputModuleDir` + `InModuleScope` are being migrated in the issue series #200..#207. While migration is in progress, `project.json``CodeCoverage.Enabled` is `false` so the percentage gate does not fail. The gate is re-enabled in issue #207 once enough tests use the mirrored pattern.
32
+
33
+
## Cross-cutting tests are still allowed
34
+
35
+
Use a cross-cutting test file (not a mirrored one) when the behavior under test truly spans multiple source files:
- Public command surface tests that exercise the built module end-to-end
39
+
- Workflow tests that intentionally validate multi-helper orchestration
40
+
- CLI route/forwarding tests that prove the routing topology, not the helpers it routes to
41
+
42
+
Cross-cutting files should be **named for the behavior** they validate (e.g., `ArchitectureGuardrails.Tests.ps1`), not for "remaining coverage". A test belongs in a mirrored file when it covers a single source file's behavior.
A non-blocking mirror status helper is available at `scripts/build/Get-TestMirrorStatus.ps1` to show which source files still lack a mirrored test.
53
+
11
54
## Test expectations
12
55
13
56
- Behavior changes require Pester coverage.
@@ -27,25 +70,28 @@ Use this file when changing production code, tests, coverage behavior, or CI tes
27
70
28
71
## Repository test structure
29
72
30
-
-`tests/NovaCommandModel*.Tests.ps1` - public command, CLI, and workflow behavior
73
+
-`tests/public/<Name>.Tests.ps1`, `tests/private/<domain>/<Name>.Tests.ps1`, `tests/classes/<Name>.Tests.ps1` - mirrored unit tests (preferred for new and migrated tests)
31
74
-`tests/ArchitectureGuardrails.Tests.ps1` - layering and adapter boundaries
32
-
-`tests/*TestSupport.ps1` - shared helpers and reusable fixtures
33
-
-Source-mirrored tests should use `tests/public/<Name>.Tests.ps1`, `tests/private/<domain>/<Name>.Tests.ps1`, and `tests/classes/<Name>.Tests.ps1` for matching `src/public/`, `src/private/`, and `src/classes/` files.
34
-
-Repeated setup belongs in `tests/TestHelpers/` or `tests/*TestSupport.ps1`, not in duplicated blocks across mirrored tests.
75
+
-`tests/NovaCommandModel*.Tests.ps1` - public command, CLI, and workflow behavior (legacy bucket, being migrated)
-`tests/CoverageGaps*.Tests.ps1`, `tests/Remaining*Coverage*.Tests.ps1` - legacy coverage buckets being retired in issue #207
35
78
36
79
## Coverage and CodeScene
37
80
38
81
- CI coverage is generated by `./scripts/build/ci/Invoke-NovaModuleToolsCI.ps1`.
39
82
-`Test-NovaBuild` runs once and produces a JaCoCo coverage report at `artifacts/coverage.xml`.
40
83
- The JaCoCo artifact is reused by the CodeScene PR coverage gate and by the develop/manual CodeScene analysis flow.
41
84
- The CodeScene analysis upload sends coverage twice: once for `line-coverage` and once for `branch-coverage`.
85
+
- Coverage paths in `project.json` must point at `src/**/*.ps1`, not at the built `dist` psm1. Nova does not override `CodeCoverage.Path`.
86
+
- During the test-layout migration, `CodeCoverage.Enabled` is temporarily `false` so the percentage gate does not block work. It is re-enabled in issue #207.
42
87
- If CodeScene flags coverage or duplication, fix the underlying test design instead of suppressing the warning casually.
43
88
44
89
## Common pitfalls
45
90
46
-
- Many tests expect the built module under `dist/NovaModuleTools`; build first when needed.
91
+
- Importing the built `dist/NovaModuleTools` module from a new mirrored test - mirrored tests dot-source `src/**/*.ps1` files directly instead.
92
+
- Using `InModuleScope NovaModuleTools { ... }` in new tests - the function under test is already in scope after dot-sourcing.
47
93
- Direct `Invoke-Pester` runs can hide Nova-specific build/import/StrictMode behavior and should not be used as the project test entrypoint.
48
-
- Some support helpers must be dot-sourced and re-exported inside `BeforeAll`.
94
+
- Some legacy support helpers must be dot-sourced and re-exported inside `BeforeAll`; new TestHelpers should expose dot-source helpers directly.
49
95
- Duplicated test setup can lower Code Health even when tests pass.
50
96
- Tests that only cover the happy path can miss the edge cases that caused the change in the first place.
51
97
- Shared mutable state between tests makes failures order-dependent and unreliable.
Copy file name to clipboardExpand all lines: .github/skills/pester-testing/SKILL.md
+26-10Lines changed: 26 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,7 +21,7 @@ Use this skill when adding tests, closing coverage gaps, fixing regressions, or
21
21
22
22
- Match existing `Describe` / `It` naming style.
23
23
- Prefer support helpers for repeated setup.
24
-
-Build/import the dist module when the test file expects it.
24
+
-For new and migrated tests, dot-source `src/**/*.ps1` files directly in `BeforeAll`. Do not `Import-Module $project.OutputModuleDir` in mirrored unit tests, and do not use `InModuleScope NovaModuleTools { ... }` - the function under test is already in scope after dot-sourcing.
25
25
- Use `Test-NovaBuild` as the authoritative test entrypoint in Nova-managed projects. Do not validate with direct `Invoke-Pester`, because it can miss the Nova build/import/StrictMode flow.
26
26
- Add coverage for both happy paths and explicit warnings/errors when behavior changed.
27
27
- For every new or changed `src/**/*.ps1` file, add or update one focused test file that mirrors the source path under `tests/`.
@@ -30,19 +30,35 @@ Use this skill when adding tests, closing coverage gaps, fixing regressions, or
30
30
- Keep shared setup in `tests/TestHelpers/` or `*TestSupport.ps1`; do not hide unrelated source-file coverage in broad catch-all test files.
31
31
- If a mirrored test is not practical because the behavior is genuinely cross-cutting, document the reason in the handoff and point to the owning integration or guardrail test.
It 'leaves CodeCoverage.Path to project.json when coverage is enabled' {
43
+
# ...
44
+
}
45
+
}
46
+
```
47
+
33
48
## Common pitfalls
34
49
35
-
- Forgetting that many tests assume `dist/NovaModuleTools` already exists
36
-
- Duplicating setup instead of extending `*.TestSupport.ps1`
37
-
- Exporting helper functions at the wrong time in test lifecycle
38
-
- Validating a Nova-managed project with direct `Invoke-Pester` instead of `Test-NovaBuild`
39
-
- Passing tests while still degrading Code Health through duplication
40
-
- Grouping unrelated source files into one large test file when a source-mirrored layout would make ownership clearer
41
-
- Adding source files without a matching mirrored test or an explicit cross-cutting-test justification
42
-
- Ignoring boundary/unhappy-path coverage or test isolation and relying on happy-path-only verification
50
+
- Importing the built `dist/NovaModuleTools` module from a new mirrored test - dot-source the relevant `src/**/*.ps1` files instead.
51
+
- Using `InModuleScope NovaModuleTools { ... }` in new mirrored tests - the function is already in scope after dot-sourcing.
52
+
- Duplicating setup instead of extending `*.TestSupport.ps1` or `tests/TestHelpers/`.
53
+
- Exporting helper functions at the wrong time in test lifecycle.
54
+
- Validating a Nova-managed project with direct `Invoke-Pester` instead of `Test-NovaBuild`.
55
+
- Passing tests while still degrading Code Health through duplication.
56
+
- Grouping unrelated source files into one large test file when a source-mirrored layout would make ownership clearer.
57
+
- Adding source files without a matching mirrored test or an explicit cross-cutting-test justification.
58
+
- Ignoring boundary/unhappy-path coverage or test isolation and relying on happy-path-only verification.
43
59
44
60
## Verification
45
61
46
62
- Run `Test-NovaBuild`
47
63
- Run `./run.ps1` before finishing code changes
48
-
- If coverage is the goal, inspect `artifacts/pester-coverage.cobertura.xml`via the CI helper flow
64
+
- If coverage is the goal, inspect `artifacts/coverage.xml`produced by the CI helper flow. Coverage is JaCoCo and references source files under `src/**/*.ps1` directly.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+4-1Lines changed: 4 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,9 +21,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
21
21
- When Git is enabled during either scaffold flow, Nova now creates or updates a default `.gitignore` in the generated project root and appends only the missing Nova-managed artifact entries instead of overwriting existing ignore rules.
22
22
-`Initialize-NovaModule` and `% nova init` now check for newer NovaModuleTools releases before the first interactive scaffold question and reuse the same non-blocking update warning behavior already used by build.
23
23
-`Test-NovaBuild` and `% nova test` now enforce `Pester.CodeCoverage.CoveragePercentTarget` from `project.json` when `CodeCoverage.Enabled` is `true`, failing the test run when measured coverage drops below the configured target.
24
+
- Added a non-blocking `scripts/build/Get-TestMirrorStatus.ps1` helper that reports `src/**/*.ps1` files without a matching mirrored test file under `tests/`, to support the migration to the source-mirrored, dot-source-first test layout.
24
25
25
26
26
27
### Changed
28
+
- Test-loading guidance for NovaModuleTools' own tests and for generated Agentic Copilot scaffolds now follows a source-mirrored, dot-source-first pattern: new mirrored tests dot-source the relevant `src/**/*.ps1` files directly in `BeforeAll` instead of importing the built `dist` module or wrapping assertions in `InModuleScope`, so tests run against source files and JaCoCo coverage references real source paths.
29
+
-`project.json``Pester.CodeCoverage.Enabled` is temporarily `false` while the existing test suite migrates to the new dot-source pattern; the configured `90` percent target stays in place and is re-enabled once migration completes.
27
30
- The architect/design flow now surfaces settled vs unresolved design items before finalization, offers explicit choices for full finalization vs design-package-only handoff, clarifies how to use design notes versus the paste-ready GitHub issue draft, and requires finalization output to follow the project Markdown authoring guidance.
28
31
-`ProjectTemplate.json` and the packaged example `project.json` now include Nova's default Pester `CodeCoverage` block with `Enabled=false`, shared `src/` coverage paths, JaCoCo output, and a `90` percent target so new projects can opt into coverage without hand-authoring the configuration.
29
32
-`Invoke-NovaModuleToolsCI.ps1` now delegates the full test run to a single `Test-NovaBuild` call; the previous second `Invoke-Pester` pass and post-run Cobertura source-path remapping step have been removed.
@@ -39,7 +42,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
39
42
40
43
### Fixed
41
44
-`Test-NovaBuild` and `% nova test` no longer emit `Remove-Item -Recurse` progress bars during the Pester run; `$global:ProgressPreference` is saved and restored around the test execution so CI logs stay clean.
42
-
- Code coverage in `Test-NovaBuild` now runs against the built `dist/<ModuleName>/<ModuleName>.psm1` file instead of source files, so measured coverage percentages and line numbers align with the deployed module.
45
+
- Code coverage in `Test-NovaBuild` now runs against the source files, instead of the built `dist/<ModuleName>/<ModuleName>.psm1` file so measured coverage percentages and line numbers align with the deployed module.
Copy file name to clipboardExpand all lines: RELEASE_NOTE.md
-2Lines changed: 0 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,8 +20,6 @@ This file summarizes public cmdlet, CLI, configuration, and migration changes fo
20
20
21
21
22
22
### Fixed
23
-
- Code coverage in `Test-NovaBuild` now runs against the built `dist/` module instead of source files, so reported coverage percentages and line numbers match the deployed module.
24
-
-`Test-NovaBuild` and `% nova test` no longer emit `Remove-Item` progress bars during the Pester run.
0 commit comments