Skip to content

Testing a module requires knowing too many things #2996

Description

@nohwnd

I counted what you need to know to test a module, by walking the path a person actually takes.

To get one passing test against a public function:

  1. get Pester 5/6 loaded and not the Pester 3 that Windows ships
  2. *.Tests.ps1 naming so Invoke-Pester finds it
  3. import in BeforeAll and not at the top of the file
  4. $PSScriptRoot for the path, the working directory is not the test folder
  5. -Force, and removing a stale or differently versioned copy
  6. Describe / It / Should

To test anything internal, or to mock at all, add:

  1. InModuleScope, or -ModuleName on every Mock
  2. BeforeAll variables do not flow into InModuleScope, you need -Parameters
  3. Should -Invoke with -ParameterFilter, and the filter evaluates in module scope too
  4. mocking a command that is not installed on the machine needs a stub function first

To generate tests from the module surface, add:

  1. -ForEach is evaluated at discovery, so the module has to be imported in BeforeDiscovery as well, you import it twice
  2. $_ and hashtable key expansion rules

To run it in CI, add:

  1. configuration object, exit code, Run.Throw
  2. TestResult format and output verbosity
  3. coverage paths, breakpoints or profiler
  4. point all of it at the built module and not at src

6 to say hello, 10 to write a real test, 16 to ship it.

The part that bothers me

Items 3, 7, 8 and 11 are not four problems, they are one problem seen four times. We have two phases, discovery and run, and two scopes, test and module. The user has to hold that 2x2 in their head and put every line of the test file into the right cell. Nothing in the API tells them the grid is there. They find out when they get $null.

How common is the simple case

I checked, because most of the proposals below only work when a repo has one module. 400 repos tagged powershell-module, kept the ones that have Pester test files and at least one .psd1 whose content declares RootModule or ModuleToProcess. n=171.

modules in repo repos
1 156 (91%)
2 10 (97% cumulative)
3 2
5, 10, 12 1 each

Most of the 2 module ones are not two products, they are one module plus a vendored dependency, a build helper, a template placeholder, or a second manifest for the same module. Genuinely multi module products were 4 of 171.

The content check matters. A first pass that used only path shape counted Pode's src/Locales/*/Pode.psd1 and PSKoans' drafts/ fixtures as modules, and reported 50%.

Proposals

Filed separately so they can be taken or dropped one by one.

Separately, #2992 came out of the same investigation, mocks lose parameter defaults, ArgumentCompleter and custom attributes.

@fflaten

🤖

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