Configuring Pester with New-PesterConfiguration
- PowerShell
- Pester
- Testing
- Configuration
- Beginners
- Automation
Passing a flag or two to Invoke-Pester is fine. Passing ten of them, every time, from memory, isn't. Once you want detailed output and a tag filter and coverage and a results file, the command line turns into a wall of parameters you copy-paste and inevitably get wrong. Pester 5 has a better answer: a single configuration object you build once, set piece by piece, save in a script, and reuse everywhere from your laptop to CI. This post is the one that makes every later automation step click into place.
What you'll learn
- How to create a configuration object with
New-PesterConfiguration - The key sections:
Run,Output,Filter,CodeCoverage, andTestResult - How to run with
Invoke-Pester -Configuration $cfg - How to store a config in a script for repeatable runs
- Why Pester 5 favors this object over splatted parameters
Start with the object
New-PesterConfiguration hands you a fully-populated configuration object with every setting already at its default. You then tweak only what you care about.
$config = New-PesterConfiguration
$configPrinting it shows the sections: Run, Filter, CodeCoverage, TestResult, Should, Debug, and Output. Each section holds typed properties. Because defaults are already in place, you never start from a blank slate, you start from "sensible" and adjust.
Run: what to execute
The Run section controls what Pester runs and what it gives back.
$config = New-PesterConfiguration
$config.Run.Path = './Tests' # file or folder to discover
$config.Run.PassThru = $true # return a result object
$result = Invoke-Pester -Configuration $config
$result.Result # 'Passed' or 'Failed'Run.Path accepts a single path or an array of paths, so you can target one file while developing and a whole folder in CI. Run.PassThru returns the rich result object, which you will lean on for thresholds and reporting.
Output: how much you see
Output.Verbosity decides how chatty the run is. The useful values are None, Normal, Detailed, and Diagnostic.
$config = New-PesterConfiguration
$config.Run.Path = './Tests'
$config.Output.Verbosity = 'Detailed'
Invoke-Pester -Configuration $configDetailed prints every Describe, Context, and It with indentation that mirrors your structure, which is exactly what you want while developing. In CI you often drop to Normal to keep logs short, and reach for Diagnostic only when you are debugging discovery itself.
Filter: which tests run
If you tagged tests back in Part 16, the Filter section is how you act on those tags through configuration. This is the v5 way; the v4 habit of passing -Tag on the command line does not map cleanly onto the configuration object.
$config = New-PesterConfiguration
$config.Run.Path = './Tests'
$config.Filter.Tag = 'Unit' # run only Unit-tagged tests
$config.Filter.ExcludeTag = 'Slow' # but never the Slow ones
Invoke-Pester -Configuration $configFilter.Tag and Filter.ExcludeTag both accept arrays, and ExcludeTag wins when a test matches both. You can also filter by Filter.FullName (the Describe/It text) or Filter.Line for surgical, single-test runs.
CodeCoverage and TestResult: the artifacts
These two sections produce the files automation cares about. Coverage we met in Part 22; TestResult writes a machine-readable summary of pass/fail that CI dashboards can publish.
$config = New-PesterConfiguration
$config.Run.Path = './Tests'
$config.CodeCoverage.Enabled = $true
$config.CodeCoverage.Path = './src'
$config.CodeCoverage.OutputPath = './coverage.xml'
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = 'NUnitXml'
$config.TestResult.OutputPath = './testResults.xml'
Invoke-Pester -Configuration $configAfter this run you have coverage.xml (JaCoCo) and testResults.xml (NUnit), the two artifacts the CI post (Part 24) publishes.
Putting it together in a script
The real payoff is saving the whole thing in a script, say run-tests.ps1, so every run is identical and lives in version control. No more remembering flags.
# run-tests.ps1 — the one true way to run this repo's tests
$config = New-PesterConfiguration
$config.Run.Path = "$PSScriptRoot/Tests"
$config.Run.PassThru = $true
$config.Output.Verbosity = 'Detailed'
$config.CodeCoverage.Enabled = $true
$config.CodeCoverage.Path = "$PSScriptRoot/src"
$config.CodeCoverage.OutputPath = "$PSScriptRoot/coverage.xml"
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = 'NUnitXml'
$config.TestResult.OutputPath = "$PSScriptRoot/testResults.xml"
$result = Invoke-Pester -Configuration $config
if ($result.FailedCount -gt 0) {
throw "$($result.FailedCount) test(s) failed."
}Now anyone, including your CI runner, gets the exact same behavior by typing ./run-tests.ps1. Using $PSScriptRoot keeps the paths correct no matter where the script is invoked from.
Why the object beats splatted parameters
You can still splat a hashtable of legacy parameters at Invoke-Pester, but in v5 that path is deprecated and, worse, you cannot mix it with -Configuration. The object wins for concrete reasons: it is strongly typed (so a typo in a section name errors instead of silently doing nothing), it is discoverable (print it and read every option), it is reusable across machines, and it is version-controllable. One object, one source of truth, zero "what flags did we use last time?"
Try it yourself
Take a Pester command line you actually run, flags and all, and reproduce it as a single configuration object in a run-tests.ps1 script. Set each flag's equivalent property, add PassThru, and throw on FailedCount. Run the script and confirm you get the same result as your old command, then delete the old command from your notes. You now have a repeatable, shareable test run.
Common mistakes
Mixing legacy parameters with
-Configuration. Pester 5 will not let you combine the old splatted parameter set with the configuration object. Pick the object and commit to it.Not knowing the defaults. Every section already has a default value. Print
New-PesterConfigurationand read it once so you only override what truly needs changing, rather than re-stating defaults.Editing config after invoking. Set every property before you call
Invoke-Pester. Changing the object afterward does nothing to a run that has already finished, set it, then invoke.
Recap
New-PesterConfiguration gives you one typed object that replaces a fistful of command-line flags. Set Run.Path for what to execute, Output.Verbosity for how much to see, Filter.Tag/ExcludeTag for which tests, and CodeCoverage/TestResult for the artifacts automation needs, then run it with Invoke-Pester -Configuration $cfg. Save the whole thing in a script with $PSScriptRoot-based paths and you have a repeatable, version-controlled run that behaves identically everywhere. Set it before you invoke, and never mix it with the legacy parameters.
Next up: Part 24 — Running Pester in CI/CD (GitHub Actions & Azure Pipelines), where we take this exact configuration object and wire it into a pipeline that runs your tests on every push and fails the build on red.