Pester 5 Configuration settings

Since Pester v5 was released this legacy warning appears every time a Pester test is performed with the -CodeCoverage flag being used.

WARNING: You are using Legacy parameter set that adapts Pester 5 syntax to Pester 4 syntax. This parameter set is deprecated, and does not work 100%. The -Strict and -PesterOption parameters are ignored, and providing advanced configuration to -Path (-Script), and -CodeCoverage via a hash table does not work. Please refer to https://github.com/pester/Pester/releases/tag/5.0.1#legacy-parameter-set for more information.

The link provided in the warning message isn’t particularly helpful, so how do we get rid of the legacy notification?

New-PesterConfiguration

It is now possible to create a Pester configuration where you can set all the required settings. I have created a configuration that generates a test result and a code coverage file, which can be used in Azure DevOps pipelines. On top of that, some extra code is added to install Pester and invoke the tests.

Unfortunately Pester V5 contains a bug that makes the code coverage output incompatible with Azure DevOps. A workaround has been added to the script and the bug is reported to the Pester GitHub project.

Param (
    [ValidateNotNullOrEmpty()][version]$pesterVersion
)

try {
    #Install Pester
    [version]$currentPesterVersion = (Get-Module -Name Pester -ListAvailable | Select-Object -First 1).Version
    if ($currentPesterVersion -notlike "$pesterVersion*") {
        Write-Host "Installing Pester..."
        Install-Module -Name Pester -RequiredVersion $pesterVersion -Force -ErrorAction Stop
    }
    Write-Host "Using Pester version $((Get-Module -Name Pester -ListAvailable | Select-Object -First 1).Version)"

    #Set Root Location
    $rootFolder = Split-Path $PSScriptRoot -Parent
    Set-Location $rootFolder

    #Create Pester configuration.
    $pesterConfiguration = @{
        Run = @{
            Path = @("$rootFolder\Interfaces")
        }
        Should = @{
            ErrorAction = 'Continue'
        }
        CodeCoverage = @{
            OutputFormat = 'JaCoCo'
            OutputEncoding = 'UTF8'
            OutputPath = "$rootFolder\Pester-Coverage.xml"
            Enabled = $true
        }
        TestResult = @{
            OutputPath = "$rootFolder\Pester-Test.xml"
            OutputFormat = 'NUnitXml'
            OutputEncoding = 'UTF8'
            Enabled = $true
        }
    }

    #Invoke pester with the configuration hashtable
    $config = New-PesterConfiguration -Hashtable $pesterConfiguration
    Invoke-Pester -Configuration $config

    #Hacking the Codecoverage file and add the Interfaces path.
    #This is needed because $pesterConfiguration.Run.Path is set to the Interfaces folder but the running folder is not
    #Normally we would use $pesterConfiguration.Run.ExcludePath however this function does not work since Pester 5.x
    [xml]$pesterCoverageOut = get-content -path "$rootFolder\Pester-Coverage.xml"
    foreach ($classNode in $pesterCoverageOut.SelectNodes("//class")) {
        $classNode.sourcefilename = "Interfaces/$($classNode.sourcefilename)"
    }
    foreach ($sourceFileNode in $pesterCoverageOut.SelectNodes("//sourcefile")) {
        $sourceFileNode.name = "Interfaces/$($sourceFileNode.name)"
    }
    $pesterCoverageOut.Save("$rootFolder\Pester-Coverage.xml")
} 
catch {
    Write-Host "##vso[task.logissue type=error]An Error occurred`: $($_)"
    Write-Host "##vso[task.complete result=Failed;]Script failed"
}

Azure Devops YAML template

I will share my Azure DevOps YAML template as a bonus, in case you want to start using Pester in combination with Azure DevOps as well :-).
Make sure you place the Pester configuration in the folder /Tests/ to get everything to work.

steps:
- task: PowerShell@2
  displayName: '[Code] Analyze'
  inputs:
    pwsh: true
    filePath: $(System.DefaultWorkingDirectory)/Tests/CodeControl.ps1
    arguments: -pesterVersion $(pesterVersion)

- task: PublishTestResults@2
  inputs:
    testResultsFormat: 'NUnit'
    testResultsFiles: '**/Pester-Test.xml'
    failTaskOnFailedTests: true  

- task: PublishCodeCoverageResults@1
  inputs:
    codeCoverageTool: 'JaCoCo'
    summaryFileLocation: '**/Pester-Coverage.xml'
    pathToSources: $(System.DefaultWorkingDirectory)
    failIfCoverageEmpty: true

This should give you the following tabs in your Azure Devops pipeline;

Tests result
Code Coverage

Sources