Pester-format tests (still supported)
Before 3.0 every Maester test was a Pester It block in a *.Tests.ps1 file. Custom tests written that way
keep running in Maester 3.0: Maester calls the real Invoke-Pester for them, and their rows appear in the same
report as the native tests. New tests should be native tests. This page covers what you need to
keep your existing Pester tests working and how to convert them.
What you needโ
-
Pester 5.7.1 or later, including 6.x. Maester no longer installs Pester or depends on it, and imports it only when the run includes Pester-format tests.
Install-Module Pester -MinimumVersion 5.7.1 -Scope CurrentUser -SkipPublisherCheck -
If the run includes Pester-format tests and no suitable Pester is installed, those tests are not run. Each one the run would have selected becomes an
Errorrow with reasonPesterNotAvailableand an install hint, Maester writes one warning, and the native tests still run and report normally.
The Pester 3.4 that ships with Windows is never used.
How Pester-format tests runโ
- Maester finds
*.Tests.ps1files under-Path, the same folder it searches for native tests, and runs them with oneInvoke-Pestercall after the native tests. - The test files run inside the Maester module, as in 2.x, so
BeforeAll,BeforeDiscovery,-ForEach,-Skip,Context,Set-ItResultand everyShouldoperator work as before. Even with-DryRun, Pester discovery runs, soBeforeDiscoveryandDescribe-level code execute. - The test ID is the text before the first colon of the
Itname:It 'CONTOSO.1001: Guest access is restricted'has the IDCONTOSO.1001. Name your tests this way so they can be selected with-TestId, configured by ID inTestSettings, and recognised in the result. - Selection by ID,
TestSettings[].EnabledandSelection.DefaultAction = Skipapply to Pester tests too. Maester reads the files statically and excludes the deselectedItblocks by line; they appear asNotRunrows with a reason code. A test whose name is built at run time (for example with<_>or$_from-ForEach) can be selected only as a whole, through the literal start of its name. -Tagand-ExcludeTagselect Pester tests by their Pester tags, as in 2.x, and thePreviewandLongRunningtags keep their default exclusions.- A Pester test has no
Product,FeatureorArea, so a run that selects with-Product,-Featureor-Arealeaves it out (NotRun, reasonNotSelected).-Suitematches the suite of its folder (Customunless asuite.jsonsays otherwise), and-Severitymatches only a severity set inTestSettings. Convert the test to the native format to give it these attributes. Severitycomes from aTestSettingsrow in the config, else, when the test callsAdd-MtTestResultDetail, from its-Severityor aSeverity:<level>tag, as in 2.x.- Rows from Pester tests have
Format = Pesterin the result. They keep the 2.x result rules; for example a failedShouldisFailed.Blockis theDescribeorContextname andTagthe Pester tags, as in 2.x. The attribute fields of a native row (Product,Feature,Areaand the others) are present and null.
Copies of built-in tests are not runโ
A Pester test whose ID is the ID of a test that ships with Maester, a previous ID of one (such as the 2.x
MS.AAD.7.1 form of a CISA ID), or the ID of a built-in that was retired, is a stale copy, and so is a test
whose name starts with the ID of a built-in family and a dot (MT.1024., MT.1033., MT.1034., MT.1059.,
MT1060.). A stale copy is not run and
produces no row, wherever it is, including under custom/. Maester writes one warning naming the files, and
lists the tests under Selection.Superseded in the result. Update-MaesterTests deletes files that contain
only such copies (outside custom/).
Copying a built-in test into custom/ to change it no longer works. Set its parameters or severity in the
run configuration, or copy it under your own ID and disable the
built-in.
Calling built-in check functionsโ
In 2.x a custom test could call a check function such as Test-MtCaMfaForRiskySignIn directly. In 3.0 those
functions are internal and no longer check their own connections. Run the check through the engine instead:
Describe 'Contoso' -Tag 'Contoso' {
It 'CONTOSO.1003: Risky sign-ins require MFA' {
$row = Invoke-MtTest -Id MT.1012
$row.Result | Should -Be 'Passed'
}
}
If Maester 2.x is still installed, calling a 2.x function makes PowerShell load the 2.x module into the session.
Maester detects this, removes it again and warns. Remove 2.x with
Uninstall-Module Maester -MaximumVersion 2.99.99 -AllVersions.
-PesterConfigurationโ
Invoke-Maester -PesterConfiguration still exists. It accepts a [PesterConfiguration] object (from
New-PesterConfiguration) or a hashtable, so a script can pass it without loading Pester first. Maester works on
a copy, so you can reuse the same object for several runs:
Invoke-Maester -PesterConfiguration @{
Run = @{ Path = './maester-tests' }
Filter = @{ Tag = 'CA'; ExcludeTag = 'App' }
}
Some options also apply to native tests:
| Option | Effect in 3.0 |
|---|---|
Run.Path | Used as -Path when -Path is not given. |
Filter.Tag | Used as -Tag when -Tag is not given. Applies to native and Pester tests. |
Filter.ExcludeTag | Added to -ExcludeTag, Selection.ExcludeTag and the default exclusions. (2.x discarded it whenever a default exclusion applied.) |
TestResult.Enabled, .OutputPath, .OutputFormat | Maester writes one NUnit or JUnit file for native and Pester rows, unless Output.TestResult in the run configuration already asks for one. See Result schema. |
Output.Verbosity | Replaced by -Verbosity. |
Run.ExcludePath, Filter.ExcludeLine | Kept, and Maester's own exclusions are added. |
Run.PassThru | Always on. |
Run.Parallel, Run.FailOnNullOrEmptyForEach (Pester 6) | Always off: parallel Pester runs outside the module and would lose every result detail. |
| Everything else | Passed to Pester for the Pester-format tests. |
Converting to native testsโ
Convert-MtTest converts the Pester-format tests in a folder to native tests. It reads the files' syntax trees
and never runs them.
Convert-MtTest -Path ./custom -WhatIf # what would be converted
Convert-MtTest -Path ./custom | Format-Table Id, Status, Notes
Convert-MtTest -Path ./custom -OutputPath ./custom/native # write the native files elsewhere
For each It block it writes Test.<ID>.ps1 and Test.<ID>.md and returns one report row with Id,
Source (file and line), Output, Status (Converted, NeedsReview or Skipped) and Notes.
What it handles:
- The split-file pattern: an
Itwhose body is one call to a function defined in a.ps1file in the same folder, checked withShould -Be $true/-BeTrue(or$false/-BeFalse). The function becomes the native test. A leadingif (-not (Test-MtConnection X)) { ... return }guard becomesService = 'X', and an outertry/catchthat only reports-SkippedBecause Erroris removed. When theItpasses arguments or asserts$false, the function is kept as a helper and a small test function calls it. - Inline tests: the
Itbody becomes the function body, with a trailing<value> | Should -Be $truerewritten toreturn (<value>). Other assertions andSet-ItResultare kept with aTODOcomment, and the row isNeedsReview. - The
Itname gives the ID and title. TheDescribename becomesFeature, so theBlockof the test's result rows, and with it the name of its test case in CI, stays what it was. ADescribename that is not one line of at most 60 characters is not written, and the report says so. - The
Describe,ContextandIttags becomeTag. APreviewtag becomesLifecycle = 'Preview'and aLongRunningtag theLongRunningflag. ASeverity:<level>tag setsSeverity, elseMedium. ASee https://...suffix that does not point to maester.dev becomesHelpUrl. Product,Areaand the other classification attributes are not guessed. Add them by hand if you want the test to be selected with-Productor-Area; see the attribute rubric. Once the test has aProduct, itsBlockis the product's name.- The Markdown comes from the function's
.mdfile, else from-Descriptionin the code, else the title; a generated.mdis noted in the report so you can add remediation steps. The<!--- Results --->footer is added when missing.
What it leaves to you:
- A test whose name is built at run time (from
-ForEachor-TestCases) isSkipped. Rewrite it by hand as a family with anInstanceSourcefunction. - Data prepared in
BeforeDiscoveryorBeforeAllis not available to the converted test; the report notes it. - An ID that is not valid for native tests is
Skipped. An ID without a prefix (such asCT0001) converts, and the report suggests one. - The guessed
Serviceis a suggestion when the code had noTest-MtConnectionguard; check it.
Each written file is validated, and validation problems turn the row into NeedsReview. Run
Get-MtTest -Path ./custom and Invoke-MtTest -Path ./custom/Test.<ID>.ps1 on the result.
The original *.Tests.ps1 files are not changed. Once a native test exists with the same ID, the Pester test
with that ID no longer runs: the native test runs instead, and the Pester test is listed under
Selection.Superseded. Delete the
Pester file when you are satisfied with the native version.
Pester 6 notesโ
Maester supports Pester 6 for custom tests. Three Pester 6 changes can affect your tests: -Tag None is
reserved by Pester, Set-ItResult -Pending is removed, and non-string values in <...> name templates render
differently.