Monkey365 uses Pester 6.0.1 for tests and PSScriptAnalyzer for static analysis. The scripts in this directory provide the same entry points for local runs and CI pipelines.
PowerShell 7.4 or newer is recommended. Install the versions used by the pipeline before running the scripts:
Install-Module Pester -RequiredVersion 6.0.1 -Scope CurrentUser -Force
Install-Module PSScriptAnalyzer -RequiredVersion 1.24.0 -Scope CurrentUser -ForceStart a new PowerShell session if another major Pester version is already loaded.
| Path | Purpose |
|---|---|
module/ |
Module import, manifest, configuration, and public API tests |
unit/ |
Deterministic function and nested-module tests |
smoke/ |
Credential-free command smoke tests |
integration/ |
Tests that can require network access or external services |
Pester discovers files ending in .Tests.ps1. Invoke-Tests.ps1 does not
choose test directories automatically; -TestPath is mandatory.
Run the credential-free suites from the repository root:
./tests/Invoke-Tests.ps1 `
-TestPath ./tests/module,./tests/unit,./tests/smoke `
-SummaryRun one directory or test file:
./tests/Invoke-Tests.ps1 -TestPath ./tests/unit/modules/monkeyhtml
./tests/Invoke-Tests.ps1 `
-TestPath ./tests/unit/modules/monkeyhtml/New-HtmlTag.Tests.ps1 `
-Verbosity DetailedWhen the current directory is tests, use paths relative to that directory:
./Invoke-Tests.ps1 -TestPath ./ -Summary `
-Output ../pester-results `
-CI -TestSuiteName monkey365-pester `
-CodeCoverage -CodeCoveragePath ../src/monkey365/core/modules/| Parameter | Type | Behavior |
|---|---|---|
-TestPath |
string[] |
Required test files or directories. |
-Summary |
switch |
Writes pester-summary.json. |
-Output |
DirectoryInfo |
Artifact directory. Defaults to pester-results at the repository root. |
-CI |
switch |
Enables test-result XML output. |
-TestSuiteName |
string |
Suite name in XML and summary output. Defaults to Monkey365-PowerShell-Tests. |
-TestOutputFormat |
string |
NUnitXml, NUnit2.5, NUnit3, or JUnitXml; defaults to NUnit2.5. |
-CIFormat |
string |
GithubActions, AzureDevops, or Auto; defaults to Auto. |
-Verbosity |
string |
Diagnostic, Detailed, or None; defaults to None. |
-CILogLevel |
string |
Error or Warning; defaults to Error. |
-CodeCoverage |
switch |
Enables coverage collection and coverage.xml. |
-CodeCoveragePercentTarget |
int |
Coverage threshold; defaults to 45. |
-CodeCoveragePath |
string[] |
Source directories to instrument. Defaults to src/monkey365. |
-CodeCoverageOutputFormat |
string |
JaCoCo or Cobertura; defaults to JaCoCo. |
-IncludeTag |
string[] |
Includes matching Pester tags. -Tag is an alias. |
-ExcludeTag |
string[] |
Excludes matching Pester tags. |
No include or exclude tags are applied unless they are passed explicitly.
The output directory is created when needed. The runner writes:
| Artifact | Condition |
|---|---|
pester-failures.json |
Every run that reaches Pester result processing |
pester-summary.json |
-Summary |
pester-results.xml |
-CI |
coverage.xml |
-CodeCoverage |
The failure report contains TestName, FilePath, ErrorMessage, and
StackTrace for each failed test. The summary includes test counts, duration,
suite name, Pester version, and coverage data when coverage is enabled.
The runner exits with code 1 when one or more tests fail and code 0 when all
tests pass. This exit behavior is independent of -CI; -CI controls the XML
test-result artifact.
Measure the MonkeyHtml module and require 80 percent coverage:
./tests/Invoke-Tests.ps1 `
-TestPath ./tests/unit/modules/monkeyhtml `
-Summary `
-CodeCoverage `
-CodeCoveragePath ./src/monkey365/core/modules/monkeyhtml `
-CodeCoveragePercentTarget 80-Path and -ModuleName are mandatory. Add -Recurse when nested source
directories must be scanned.
Analyze the complete source tree and return PSScriptAnalyzer diagnostic records:
./tests/Invoke-Analyzer.ps1 `
-Path ./src/monkey365 `
-Recurse `
-ModuleName monkey365Generate an NUnit report for CI:
./tests/Invoke-Analyzer.ps1 `
-Path ./src/monkey365 `
-Recurse `
-OutputFormat Nunit `
-ModuleName monkey365 `
-Output ./psaResultsFileGenerate a SARIF 2.1.0 report for GitHub code scanning:
./tests/Invoke-Analyzer.ps1 `
-Path ./src/monkey365 `
-Recurse `
-OutputFormat SARIF `
-ModuleName monkey365 `
-Output ./PSScriptAnalyzer.sarifWhen the current directory is tests, the equivalent pipeline command is:
./Invoke-Analyzer.ps1 -Path ../src/monkey365/ -Recurse -OutputFormat Nunit `
-ModuleName monkey365 -Output psaResultsFileRelative -Output values are resolved from the repository root. If the file
already exists, pass -Force to replace it.
| Parameter | Type | Behavior |
|---|---|---|
-Path |
string[] |
Required source directories. |
-ModuleName |
string |
Required name for the NUnit project suite or SARIF automation details. |
-Recurse |
switch |
Recursively enumerates each source directory. |
-TreatSuppressedAsSkipped |
switch |
Emits fully suppressed rules as skipped NUnit cases. |
-Output |
string |
Destination file used with -OutputFormat. |
-Force |
switch |
Replaces an existing output file. |
-OutputFormat |
string |
Nunit for NUnit XML or SARIF for GitHub code scanning. If omitted, raw diagnostics are returned. |
-Severity |
string[] |
Collects Error, Warning, and/or Information diagnostics. |
-FailOnSeverity |
string[] |
Passes blocking severity selection to the analyzer configuration. |
-IncludeRule |
string[] |
Runs only the named analyzer rules. |
-ExcludeRule |
string[] |
Excludes the named analyzer rules. |
The runner analyzes .ps1, .psm1, and .psd1 files and excludes files whose
names end in Tests.ps1. Without -OutputFormat, raw diagnostic records are
returned to the caller. -OutputFormat Nunit produces NUnit XML, while
-OutputFormat SARIF produces SARIF 2.1.0. If -Output is omitted, the
serialized artifact text is returned instead.
Example with analyzer overrides:
./tests/Invoke-Analyzer.ps1 `
-Path ./src/monkey365/core/modules `
-Recurse `
-ModuleName monkey365 `
-Severity Error,Warning,Information `
-FailOnSeverity Error `
-ExcludeRule PSAvoidUsingWriteHostThe pipeline runs both commands from the tests directory and publishes the
generated NUnit and coverage files:
- task: PowerShell@2
displayName: Run Pester tests
inputs:
pwsh: true
targetType: inline
workingDirectory: '$(Build.SourcesDirectory)/tests'
script: |
./Invoke-Tests.ps1 -TestPath ./ -Summary `
-Output '$(Build.ArtifactStagingDirectory)' `
-CI -TestSuiteName monkey365-pester `
-CodeCoverage -CodeCoveragePath ../src/monkey365/core/modules/
- task: PowerShell@2
displayName: Run PSScriptAnalyzer
inputs:
pwsh: true
targetType: inline
workingDirectory: '$(Build.SourcesDirectory)/tests'
script: |
./Invoke-Analyzer.ps1 -Path ../src/monkey365/ -Recurse `
-OutputFormat Nunit `
-ModuleName monkey365 `
-Output '$(Build.ArtifactStagingDirectory)/PSScriptAnalyzer.xml'The configuration script returns a Pester 6 PesterConfiguration object:
$configuration = & ./tests/monkey365pester.config.ps1 `
-TestPath ./tests/unit
Invoke-Pester -Configuration $configurationDirect configuration invocation bypasses the runner's failure and summary JSON reports and its explicit exit-code handling.
- Place the test in the directory matching its purpose.
- Name it
<subject>.Tests.ps1. - Keep module, unit, and smoke tests deterministic and credential-free.
- Place network or service-dependent tests under
integration/and tag them. - Run the narrow test path first, followed by the required suite paths.
New tests should use Pester 6 assertions and Should-Invoke for mock call
verification.