Skip to content

Infer the module under test instead of making people write -ModuleName #2997

Description

@nohwnd

Today you write -ModuleName MyModule on every Mock, or you wrap the test in InModuleScope. Both are things you have to know about before you can write a working test, and the people who get this wrong are exactly the people who have not read that page yet.

We already infer, but only in the case we want people to stop using. From Mock.ps1:

if (-not $PSBoundParameters.ContainsKey('ModuleName') -and $null -ne $SessionState.Module) {
    $ModuleName = $SessionState.Module.Name
}

That fires when the caller is already in module scope, which today means you used InModuleScope.

The rule

Watch what BeforeAll imported.

  1. snapshot Get-Module objects before the container's BeforeAll
  2. after it runs, take the modules that are not in the snapshot
  3. drop any that appear in another new module's RequiredModules
  4. exactly one left, that is the module under test, use it as the default -ModuleName
  5. zero, or more than one, bind nothing and behave exactly as today

No configuration and no new syntax, it reads the Import-Module that people already write. Every failure falls back to current behaviour, so it cannot break an existing suite.

Compare by object identity, not by name

This is the detail that decides the implementation.

by name and path by object identity
fresh import detected detected
-Force reimport of an already loaded module missed detected
two modules imported 2, refuse to guess

-Force creates a new PSModuleInfo, so identity comparison catches the reimport that a name comparison misses. That matters because reimport is the normal case once the second test file runs in the same session. A name based check would bind nothing exactly where people need it.

Dependencies

Without step 3 any module that has RequiredModules looks ambiguous. RequiredModules is populated on the loaded PSModuleInfo, so it filters cleanly:

newly loaded: 2 -> Helper, Main
required by others: Helper
candidates after filter: 1 -> Main

What I did not check

Deeper chains, A requires B requires C. The filter should hold but I did not test it.

Where the snapshot belongs in our lifecycle, container setup or the BeforeAll boundary.

This does nothing for discovery. BeforeAll runs after discovery, so -ForEach (Get-Command -Module X) still needs the module imported earlier than this.

Part of #2996.

🤖

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions