Skip to content

Anchor module-less Mock to the test scope instead of the caller's module#2821

Open
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-fix-mock-module-inference-2025
Open

Anchor module-less Mock to the test scope instead of the caller's module#2821
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-fix-mock-module-inference-2025

Conversation

@nohwnd

@nohwnd nohwnd commented Jun 30, 2026

Copy link
Copy Markdown
Member

Fix #2025

Problem

When Mock (or Should -Invoke) is called without -ModuleName from a helper function that lives in a module — for example a reusable mock-injection helper — the mock was silently scoped to that helper module instead of the test scope. As a result the mock never applied to the command under test, and the real command ran instead.

The root cause is that the target was inferred purely from the caller's session state module: any call originating inside a module inherited that module, with no way to tell an intentional in-module mock (InModuleScope M { Mock ... }) from one that merely passes through a module-defined helper.

Fix

Resolve the mock target explicitly via a shared Resolve-MockCallerScope helper used by both Mock and Should -Invoke, so they always agree on where the mock lives:

  • Intentional in-module call → inherit the module (unchanged behavior). This is detected when either:
    • we are directly inside InModuleScope <module> { ... } (InModuleScope now pushes/pops a marker for the module it runs in), or
    • the current block/test body was itself defined in that module (e.g. InModuleScope wrapping a Describe).
  • Helper-in-a-module call (the Pester creating mock in module scope when -ModuleName is not provided #2025 case) → anchor the mock to the test/script session state, exactly as a direct Mock in the test would.
  • No running test/block → historical fallback.

Tests

  • New regression Describe in tst/functions/Mock.Tests.ps1 covering the helper-injected mock applying to the test, Should -Invoke consistency from both the helper module and the test scope, and a boundary check that InModuleScope without -ModuleName still targets the module. These were verified to fail on the unpatched build and pass with the fix.
  • No existing tests needed rewriting (the in-module/InPesterModuleScope cases take the block-match path and are preserved).

Validation

Full suite green — P-tests pass plus 2496 Pester tests, 0 failed. Mock.Tests.ps1 (254) and InModuleScope.Tests.ps1 (39) all pass. Build analyzer reports no new violations in the edited source files.

When Mock or Should -Invoke is called without -ModuleName from a helper
function that lives in a module (for example a reusable mock-injection
helper), the mock was silently scoped to that helper module instead of the
test scope, so it never applied to the command under test.

Resolve the target session state explicitly: keep inheriting the module
only for intentional in-module calls (inside InModuleScope, or when the
current block/test body was itself defined in that module), and otherwise
anchor the mock to the test/script session state. InModuleScope now marks
the module it is executing in so these calls are recognised, and Mock and
Should -Invoke share the same resolution so they stay consistent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nohwnd nohwnd added this to the 6.0.0 milestone Jun 30, 2026
@nohwnd

nohwnd commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

needs careful review

@nohwnd nohwnd modified the milestones: 6.0.0, 6.0.0-future Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pester creating mock in module scope when -ModuleName is not provided

1 participant