Notices: This section not yet converted to new layout. Download stats are rolling back out.

This is not the latest version of Pester available.

Pester

3.3.1

There are no package test results for this package

This package was approved by moderator ferventcoder on 1/15/2015.

Pester provides a framework for running BDD style Tests to execute and validate PowerShell commands inside of PowerShell and offers a powerful set of Mocking Functions that allow tests to mimic and mock the functionality of any command inside of a piece of powershell code being tested. Pester tests can execute any command or script that is accesible to a pester test file. This can include functions, Cmdlets, Modules and scripts. Pester can be run in ad hoc style in a console or it can be integrated into the Build scripts of a Continuous Integration system.

To install Pester, run the following command from the command line or from PowerShell:

C:\> choco install pester --version 3.3.1

To upgrade Pester, run the following command from the command line or from PowerShell:

C:\> choco upgrade pester --version 3.3.1

Files

Hide
  • chocolateyInstall.ps1 Show
    [CmdletBinding()]
    param ( )
    
    end
    {
        $modulePath = Join-Path $env:ProgramFiles WindowsPowerShell\Modules
        $targetDirectory = Join-Path $modulePath Pester
    
        $scriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
        $sourceDirectory = Join-Path $scriptRoot Tools
    
        Update-Directory -Source $sourceDirectory -Destination $targetDirectory
    
        if ($PSVersionTable.PSVersion.Major -lt 4)
        {
            $modulePaths = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') -split ';'
            if ($modulePaths -notcontains $modulePath)
            {
                Write-Verbose "Adding '$modulePath' to PSModulePath."
    
                $modulePaths = @(
                    $modulePath
                    $modulePaths
                )
    
                $newModulePath = $modulePaths -join ';'
    
                [Environment]::SetEnvironmentVariable('PSModulePath', $newModulePath, 'Machine')
                $env:PSModulePath += ";$modulePath"
            }
        }
    }
    
    begin
    {
        function Update-Directory
        {
            [CmdletBinding()]
            param (
                [Parameter(Mandatory = $true)]
                [string] $Source,
    
                [Parameter(Mandatory = $true)]
                [string] $Destination
            )
    
            $Source = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Source)
            $Destination = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Destination)
    
            if (-not (Test-Path -LiteralPath $Destination))
            {
                $null = New-Item -Path $Destination -ItemType Directory -ErrorAction Stop
            }
    
            try
            {
                $sourceItem = Get-Item -LiteralPath $Source -ErrorAction Stop
                $destItem = Get-Item -LiteralPath $Destination -ErrorAction Stop
    
                if ($sourceItem -isnot [System.IO.DirectoryInfo] -or $destItem -isnot [System.IO.DirectoryInfo])
                {
                    throw 'Not Directory Info'
                }
            }
            catch
            {
                throw 'Both Source and Destination must be directory paths.'
            }
    
            $sourceFiles = Get-ChildItem -Path $Source -Recurse |
                           Where-Object { -not $_.PSIsContainer }
    
            foreach ($sourceFile in $sourceFiles)
            {
                $relativePath = Get-RelativePath $sourceFile.FullName -RelativeTo $Source
                $targetPath = Join-Path $Destination $relativePath
    
                $sourceHash = Get-FileHash -Path $sourceFile.FullName
                $destHash = Get-FileHash -Path $targetPath
    
                if ($sourceHash -ne $destHash)
                {
                    $targetParent = Split-Path $targetPath -Parent
    
                    if (-not (Test-Path -Path $targetParent -PathType Container))
                    {
                        $null = New-Item -Path $targetParent -ItemType Directory -ErrorAction Stop
                    }
    
                    Write-Verbose "Updating file $relativePath to new version."
                    Copy-Item $sourceFile.FullName -Destination $targetPath -Force -ErrorAction Stop
                }
            }
    
            $targetFiles = Get-ChildItem -Path $Destination -Recurse |
                           Where-Object { -not $_.PSIsContainer }
        
            foreach ($targetFile in $targetFiles)
            {
                $relativePath = Get-RelativePath $targetFile.FullName -RelativeTo $Destination
                $sourcePath = Join-Path $Source $relativePath        
    
                if (-not (Test-Path $sourcePath -PathType Leaf))
                {
                    Write-Verbose "Removing unknown file $relativePath from module folder."
                    Remove-Item -LiteralPath $targetFile.FullName -Force -ErrorAction Stop
                }
            }
    
        }
    
        function Get-RelativePath
        {
            param ( [string] $Path, [string] $RelativeTo )
            return $Path -replace "^$([regex]::Escape($RelativeTo))\\?"
        }
    
        function Get-FileHash
        {
            param ([string] $Path)
    
            if (-not (Test-Path -LiteralPath $Path -PathType Leaf))
            {
                return $null
            }
    
            $item = Get-Item -LiteralPath $Path
            if ($item -isnot [System.IO.FileSystemInfo])
            {
                return $null
            }
    
            $stream = $null
    
            try
            {
                $sha = New-Object System.Security.Cryptography.SHA256CryptoServiceProvider
                $stream = $item.OpenRead()
                $bytes = $sha.ComputeHash($stream)
                return [convert]::ToBase64String($bytes)
            }
            finally
            {
                if ($null -ne $stream) { $stream.Close() }
                if ($null -ne $sha)    { $sha.Clear() }
            }
        }
    }
    
  • tools\bin\pester.bat Show
    @echo off
    SET DIR=%~dp0%
    SET ARGS=%*
    if NOT '%1'=='' SET ARGS=%ARGS:"=\"%
    if '%1'=='/?' goto usage
    if '%1'=='-?' goto usage
    if '%1'=='?' goto usage
    if '%1'=='/help' goto usage
    if '%1'=='help' goto usage
    
    @PowerShell -NonInteractive -NoProfile -ExecutionPolicy Bypass -Command ^
     "& Import-Module '%DIR%..\Pester.psm1';  & { Invoke-Pester -OutputXml Test.xml -Strict -EnableExit %ARGS%}"
    
    goto finish
    :usage
    if NOT '%2'=='' goto help
    
    echo To run pester for tests, just call pester or runtests with no arguments
    echo.
    echo Example: pester
    echo.
    echo For Detailed help information, call pester help with a help topic. See
    echo help topic about_Pester for a list of all topics at the end
    echo.
    echo Example: pester help about_Pester
    echo.
    goto finish
    
    :help
    @PowerShell -NonInteractive -NoProfile -ExecutionPolicy Bypass -Command ^
      "& Import-Module '%DIR%..\Pester.psm1'; & { Get-Help %2}"
    
    :finish
    exit /B %errorlevel%
    
  • tools\Functions\Assertions\MatchExactly.ps1 Show
    function PesterMatchExactly($value, $expectedMatch) {
        return ($value -cmatch $expectedMatch)
    }
    
    function PesterMatchExactlyFailureMessage($value, $expectedMatch) {
        return "Expected: {$value} to exactly match the expression {$expectedMatch}"
    }
    
    function NotPesterMatchExactlyFailureMessage($value, $expectedMatch) {
        return "Expected: ${value} to not match the expression ${expectedMatch} exactly"
    }
    
    
  • tools\Functions\Assertions\Match.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "Match" {
            It "returns true for things that match" {
                PesterMatch "foobar" "ob" | Should Be $true
            }
    
            It "returns false for things that do not match" {
                PesterMatch "foobar" "slime" | Should Be $false
            }
    
            It "passes for strings with different case" {
                PesterMatch "foobar" "FOOBAR" | Should Be $true
            }
    
            It "uses regular expressions" {
                PesterMatch "foobar" "\S{6}" | Should Be $true
            }
        }
    }
    
  • tools\Functions\Assertions\Match.ps1 Show
    function PesterMatch($value, $expectedMatch) {
        return ($value -match $expectedMatch)
    }
    
    function PesterMatchFailureMessage($value, $expectedMatch) {
        return "Expected: {$value} to match the expression {$expectedMatch}"
    }
    
    function NotPesterMatchFailureMessage($value, $expectedMatch) {
        return "Expected: ${value} to not match the expression ${expectedMatch}"
    }
    
    
  • tools\Functions\Assertions\Exist.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterExist" {
            It "returns true for paths that exist" {
                Test-PositiveAssertion (PesterExist $TestDrive)
            }
    
            It "returns false for paths do not exist" {
                Test-NegativeAssertion (PesterExist "$TestDrive\nonexistant")
            }
        }
    }
    
  • tools\Functions\Assertions\Exist.ps1 Show
    function PesterExist($value) {
        return (Test-Path $value)
    }
    
    function PesterExistFailureMessage($value) {
        return "Expected: {$value} to exist"
    }
    
    function NotPesterExistFailureMessage($value) {
        return "Expected: ${value} to not exist, but it was found"
    }
    
    
    
  • tools\Functions\Assertions\ContainExactly.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterContainExactly" {
            Context "when testing file contents" {
                Setup -File "test.txt" "this is line 1`nPester is awesome"
                It "returns true if the file contains the specified content exactly" {
                    Test-PositiveAssertion (PesterContainExactly "$TestDrive\test.txt" "Pester")
                }
    
                It "returns false if the file does not contain the specified content exactly" {
                    Test-NegativeAssertion (PesterContainExactly "$TestDrive\test.txt" "pESTER")
                }
            }
        }
    }
    
  • tools\Functions\Assertions\ContainExactly.ps1 Show
    function PesterContainExactly($file, $contentExpecation) {
        return ((Get-Content $file) -cmatch $contentExpecation)
    }
    
    function PesterContainExactlyFailureMessage($file, $contentExpecation) {
        return "Expected: file ${file} to contain exactly {$contentExpecation}"
    }
    
    function NotPesterContainExactlyFailureMessage($file, $contentExpecation) {
        return "Expected: file {$file} to not contain exactly ${contentExpecation} but it did"
    }
    
    
  • tools\Functions\Assertions\BeGreaterThan.ps1 Show
    function PesterBeGreaterThan($value, $expected)
    {
        return [bool]($value -gt $expected)
    }
    
    function PesterBeGreaterThanFailureMessage($value,$expected)
    {
        return "Expected {$value} to be greater than {$expected}"
    }
    
    function NotPesterBeGreaterThanFailureMessage($value,$expected)
    {
        return "Expected {$value} to be less than or equal to {$expected}"
    }
    
  • tools\Functions\Assertions\Be.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterBe" {
            It "returns true if the 2 arguments are equal" {
                Test-PositiveAssertion (PesterBe 1 1)
            }
            It "returns true if the 2 arguments are equal and have different case" {
                Test-PositiveAssertion (PesterBe "A" "a")
            }
    
            It "returns false if the 2 arguments are not equal" {
                Test-NegativeAssertion (PesterBe 1 2)
            }
        }
        Describe "PesterBeFailureMessage" {
            #the correctness of difference index value and the arrow pointing to the correct place 
            #are not tested here thoroughly, but the behaviour was visually checked and is
            #implicitly tested by using the whole output in the following tests
            
            
            It "Returns nothing for two identical strings" {
                #this situation should actually never happen, as the code is called 
                #only when the objects are not equal
                
                $string = "string"
                PesterBeFailureMessage $string $string | Should BeNullOrEmpty
            }
        
            It "Outputs less verbose message for two different objects that are not strings" {
                PesterBeFailureMessage 2 1 | Should Be "Expected: {1}`nBut was:  {2}"
            }
            
            It "Outputs verbose message for two strings of different length" {
                PesterBeFailureMessage "actual" "expected" | Should Be "Expected string length 8 but was 6. Strings differ at index 0.`nExpected: {expected}`nBut was:  {actual}`n-----------^"
            }
            
            It "Outputs verbose message for two different strings of the same length" {
                PesterBeFailureMessage "x" "y" | Should Be "String lengths are both 1. Strings differ at index 0.`nExpected: {y}`nBut was:  {x}`n-----------^"
            }
            
            It "Replaces non-printable characters correctly" {
                PesterBeFailureMessage "`n`r`b`0`tx" "`n`r`b`0`ty" | Should Be "String lengths are both 6. Strings differ at index 5.`nExpected: {\n\r\b\0\ty}`nBut was:  {\n\r\b\0\tx}`n---------------------^"
            }
            
            It "The arrow points to the correct position when non-printable characters are replaced before the difference" {
                PesterBeFailureMessage "123`n456" "123`n789" | Should Be "String lengths are both 7. Strings differ at index 4.`nExpected: {123\n789}`nBut was:  {123\n456}`n----------------^"
            }
            
            It "The arrow points to the correct position when non-printable characters are replaced after the difference" {
                PesterBeFailureMessage "abcd`n123" "abc!`n123" | Should Be "String lengths are both 8. Strings differ at index 3.`nExpected: {abc!\n123}`nBut was:  {abcd\n123}`n--------------^"
            }
        }
    }
    
    InModuleScope Pester {
        Describe "BeExactly" {
            It "passes if letter case matches" {
                'a' | Should BeExactly 'a'
            }
            It "fails if letter case doesn't match" {
                'A' | Should Not BeExactly 'a'
            }
            It "passes for numbers" {
                1 | Should BeExactly 1
                2.15 | Should BeExactly 2.15
            }
        }
        
        Describe "PesterBeExactlyFailureMessage" {
            It "Writes verbose message for strings that differ by case" {
                PesterBeExactlyFailureMessage "a" "A" | Should Be "String lengths are both 1. Strings differ at index 0.`nExpected: {A}`nBut was:  {a}`n-----------^"
            }
        }
    }
    
    
  • tools\en-US\about_should.help.txt Show
    TOPIC
    	Should
    
    SYNOPSIS
    	Provides assertion convenience methods for comparing objects and throwing 
    	test failures when test expectations fail.
    
    DESCRIPTION
    	Should is an Extension of System.Object and can be used as a native type 
    	inside Describe blocks. The various Should member methods can be invoked 
    	directly from an object being compared. It is typically used in individual 
    	It blocks to verify the results of an expectation. The Should method is 
    	typically called from the "actual" object being compared and takes the 
    	"expected" object as a parameter. Should includes several members that 
    	perform various comparisons of objects and will throw a PesterFailure when 
    	the objects do not evaluate to be comparable.
    
    SHOULD MEMBERS
    
    	Be
    		Compares one object with another for equality and throws if the two 
    		objects are not the same.
    
            $actual="Actual value"
            $actual | Should Be "actual value" # Test will pass
            $actual | Should Be "not actual value"  # Test will fail
    
        BeExactly
            Compares one object with another for equality and throws if the two objects are not the same.  This comparison is case sensitive.
    
            $actual="Actual value"
            $actual | Should BeExactly "Actual value" # Test will pass
            $actual | Should BeExactly "actual value" # Test will fail
            
        BeGreaterThan
            Asserts that a number is greater than an expected value. Uses PowerShell's -gt operator to compare the two values.
            
            $Error.Count | Should BeGreaterThan 0
    
        BeLessThan
            Asserts that a number is less than an expected value. Uses PowerShell's -gt operator to compare the two values.
            
            $Error.Count | Should BeLessThan 1
    
    	Exist
    		Does not perform any comparison but checks if the object calling Exist 
    		is presnt in a PS Provider. The object must have valid path syntax. It 
    		essentially must pass a Test-Path call.
    
            $actual=(Dir . )[0].FullName
            Remove-Item $actual
            $actual | Should Exist # Test will fail
    
        Contain
            Checks to see if a file contains the specified text.  This search is not case sensitive and uses regular expressions. 
            
            Set-Content -Path TestDrive:\file.txt -Value 'I am a file.'
            'TestDrive:\file.txt' | Should Contain 'I Am' # Test will pass
            'TestDrive:\file.txt' | Should Contain '^I.*file$' # Test will pass
    
            'TestDrive:\file.txt' | Should Contain 'I Am Not' # Test will fail
    
            Tip: Use [regex]::Escape("pattern") to match the exact text.
    	    
            Set-Content -Path TestDrive:\file.txt -Value 'I am a file.'
            'TestDrive:\file.txt' | Should Contain 'I.am.a.file' # Test will pass
            'TestDrive:\file.txt' | Should Contain ([regex]::Escape('I.am.a.file')) # Test will fail
    
        ContainExactly
            Checks to see if a file contains the specified text.  This search is case sensitive and uses regular expressions to match the text.
    
            Set-Content -Path TestDrive:\file.txt -Value 'I am a file.'
            'TestDrive:\file.txt' | Should Contain 'I am' # Test will pass
            'TestDrive:\file.txt' | Should Contain 'I Am' # Test will fail
    
        Match
    		Uses a regular expression to compare two objects. This comparison is not case sensitive.
    
            "I am a value" | Should Match "I Am" # Test will pass
            "I am a value" | Should Match "I am a bad person" # Test will fail
    
            Tip: Use [regex]::Escape("pattern") to match the exact text.
    
            "Greg" | Should Match ".reg" # Test will pass
            "Greg" | Should Match ([regex]::Escape(".reg")) # Test will fail
    
        MatchExactly
            Uses a regular expression to compare two objects.  This comparison is case sensitive.
    
            "I am a value" | Should MatchExactly "I am" # Test will pass
            "I am a value" | Should MatchExactly "I Am" # Test will fail
    
        Throw
            Checks if an exception was thrown in the input ScriptBlock.
    
            { foo } | Should Throw # Test will pass
            { $foo = 1 } | Should Throw # Test will fail
            { foo } | Should Not Throw # Test will fail
            { $foo = 1 } | Should Not Throw # Test will pass
    
            Warning: The input object must be a ScriptBlock, otherwise it is processed outside of the assertion.
    
            Get-Process -Name "process" -ErrorAction Stop | Should Throw # Should pass, but the exception thrown by Get-Process causes the test to fail.
    
        BeNullOrEmpty
            Checks values for null or empty (strings). The static [String]::IsNullOrEmpty() method is used to do the comparison.
    
            $null | Should BeNullOrEmpty # Test will pass
            $null | Should Not BeNullOrEmpty # Test will fail
            @()   | Should BeNullOrEmpty # Test will pass
            ""    | Should BeNullOrEmpty # Test will pass
    
    USING SHOULD IN A TEST
    
    	function Add-Numbers($a, $b) {
    	    return $a + $b
    	}
    
    	Describe "Add-Numbers" {
    
    	    It "adds positive numbers" {
    	        $sum = Add-Numbers 2 3
    	        $sum | should be 3
    	    }
    	}
    
    	This test will fail since 3 will not be equal to the sum of 2 and 3.
    
    SEE ALSO
    	Describe
        Context
    	It
    
  • tools\Functions\Coverage.Tests.ps1 Show
    if ($PSVersionTable.PSVersion.Major -le 2) { return }
    
    InModuleScope Pester {
        Describe 'Code Coverage Analysis' {
            $root = (Get-PSDrive TestDrive).Root
    
            $null = New-Item -Path $root\TestScript.ps1 -ItemType File -ErrorAction SilentlyContinue
    
            Set-Content -Path $root\TestScript.ps1 -Value @'
                function FunctionOne
                {
                    function NestedFunction
                    {
                        'I am the nested function.'
                        'I get fully executed.'
                    }
    
                    if ($true)
                    {
                        'I am functionOne'
                        NestedFunction
                    }
                }
    
                function FunctionTwo
                {
                    'I am function two.  I never get called.'
                }
    
                FunctionOne
    
    '@
    
            Context 'Entire file' {
                $testState = New-PesterState -Path $root
    
                # Path deliberately duplicated to make sure the code doesn't produce multiple breakpoints for the same commands
                Enter-CoverageAnalysis -CodeCoverage "$root\TestScript.ps1", "$root\TestScript.ps1" -PesterState $testState
    
                It 'Has the proper number of breakpoints defined' {
                    $testState.CommandCoverage.Count | Should Be 7
                }
    
                $null = & "$root\TestScript.ps1"
                $coverageReport = Get-CoverageReport -PesterState $testState
    
                It 'Reports the proper number of executed commands' {
                    $coverageReport.NumberOfCommandsExecuted | Should Be 6
                }
    
                It 'Reports the proper number of analyzed commands' {
                    $coverageReport.NumberOfCommandsAnalyzed | Should Be 7
                }
    
                It 'Reports the proper number of analyzed files' {
                    $coverageReport.NumberOfFilesAnalyzed | Should Be 1
                }
    
                It 'Reports the proper number of missed commands' {
                    $coverageReport.MissedCommands.Count | Should Be 1
                }
    
                It 'Reports the correct missed command' {
                    $coverageReport.MissedCommands[0].Command | Should Be "'I am function two.  I never get called.'"
                }
    
                Exit-CoverageAnalysis -PesterState $testState
            }
    
            Context 'Single function with missed commands' {
                $testState = New-PesterState -Path $root
    
                Enter-CoverageAnalysis -CodeCoverage @{Path = "$root\TestScript.ps1"; Function = 'FunctionTwo'} -PesterState $testState
    
                It 'Has the proper number of breakpoints defined' {
                    $testState.CommandCoverage.Count | Should Be 1
                }
    
                $null = & "$root\TestScript.ps1"
                $coverageReport = Get-CoverageReport -PesterState $testState
    
                It 'Reports the proper number of executed commands' {
                    $coverageReport.NumberOfCommandsExecuted | Should Be 0
                }
    
                It 'Reports the proper number of analyzed commands' {
                    $coverageReport.NumberOfCommandsAnalyzed | Should Be 1
                }
    
                It 'Reports the proper number of missed commands' {
                    $coverageReport.MissedCommands.Count | Should Be 1
                }
    
                It 'Reports the correct missed command' {
                    $coverageReport.MissedCommands[0].Command | Should Be "'I am function two.  I never get called.'"
                }
    
                Exit-CoverageAnalysis -PesterState $testState
            }
    
            Context 'Single function with no missed commands' {
                $testState = New-PesterState -Path $root
    
                Enter-CoverageAnalysis -CodeCoverage @{Path = "$root\TestScript.ps1"; Function = 'FunctionOne'} -PesterState $testState
    
                It 'Has the proper number of breakpoints defined' {
                    $testState.CommandCoverage.Count | Should Be 5
                }
    
                $null = & "$root\TestScript.ps1"
                $coverageReport = Get-CoverageReport -PesterState $testState
    
                It 'Reports the proper number of executed commands' {
                    $coverageReport.NumberOfCommandsExecuted | Should Be 5
                }
    
                It 'Reports the proper number of analyzed commands' {
                    $coverageReport.NumberOfCommandsAnalyzed | Should Be 5
                }
    
                It 'Reports the proper number of missed commands' {
                    $coverageReport.MissedCommands.Count | Should Be 0
                }
    
                Exit-CoverageAnalysis -PesterState $testState
            }
    
            Context 'Range of lines' {
                $testState = New-PesterState -Path $root
    
                Enter-CoverageAnalysis -CodeCoverage @{Path = "$root\TestScript.ps1"; StartLine = 11; EndLine = 12 } -PesterState $testState
    
                It 'Has the proper number of breakpoints defined' {
                    $testState.CommandCoverage.Count | Should Be 2
                }
    
                $null = & "$root\TestScript.ps1"
                $coverageReport = Get-CoverageReport -PesterState $testState
    
                It 'Reports the proper number of executed commands' {
                    $coverageReport.NumberOfCommandsExecuted | Should Be 2
                }
    
                It 'Reports the proper number of analyzed commands' {
                    $coverageReport.NumberOfCommandsAnalyzed | Should Be 2
                }
    
                It 'Reports the proper number of missed commands' {
                    $coverageReport.MissedCommands.Count | Should Be 0
                }
    
                Exit-CoverageAnalysis -PesterState $testState
            }
    
            Context 'Wildcard resolution' {
                $testState = New-PesterState -Path $root
    
                Enter-CoverageAnalysis -CodeCoverage @{Path = "$root\*.ps1"; Function = '*' } -PesterState $testState
    
                It 'Has the proper number of breakpoints defined' {
                    $testState.CommandCoverage.Count | Should Be 6
                }
    
                $null = & "$root\TestScript.ps1"
                $coverageReport = Get-CoverageReport -PesterState $testState
    
                It 'Reports the proper number of executed commands' {
                    $coverageReport.NumberOfCommandsExecuted | Should Be 5
                }
    
                It 'Reports the proper number of analyzed commands' {
                    $coverageReport.NumberOfCommandsAnalyzed | Should Be 6
                }
    
                It 'Reports the proper number of analyzed files' {
                    $coverageReport.NumberOfFilesAnalyzed | Should Be 1
                }
    
                It 'Reports the proper number of missed commands' {
                    $coverageReport.MissedCommands.Count | Should Be 1
                }
    
                It 'Reports the correct missed command' {
                    $coverageReport.MissedCommands[0].Command | Should Be "'I am function two.  I never get called.'"
                }
    
                Exit-CoverageAnalysis -PesterState $testState
            }
        }
    
        Describe 'Stripping common parent paths' {
            $paths = @(
                'C:\Common\Folder\UniqueSubfolder1\File.ps1'
                'C:\Common\Folder\UniqueSubfolder2\File2.ps1'
                'C:\Common\Folder\UniqueSubfolder3\File3.ps1'
            )
    
            $commonPath = Get-CommonParentPath -Path $paths
    
            It 'Identifies the correct parent path' {
                $commonPath | Should Be 'C:\Common\Folder'
            }
    
            It 'Strips the common path correctly' {
                Get-RelativePath -Path $paths[0] -RelativeTo $commonPath |
                Should Be 'UniqueSubfolder1\File.ps1'
            }
        }
    
        if ((Get-Module -ListAvailable PSDesiredStateConfiguration) -and $PSVersionTable.PSVersion.Major -ge 4)
        {
            Describe 'Analyzing coverage of a DSC configuration' {
                $root = (Get-PSDrive TestDrive).Root
    
                $null = New-Item -Path $root\TestScriptWithConfiguration.ps1 -ItemType File -ErrorAction SilentlyContinue
    
                Set-Content -Path $root\TestScriptWithConfiguration.ps1 -Value @'
                    $line1 = $true   # Triggers breakpoint
                    $line2 = $true   # Triggers breakpoint
    
                    configuration MyTestConfig   # does NOT trigger breakpoint
                    {
                        Node localhost    # Triggers breakpoint
                        {
                            WindowsFeature XPSViewer   # Triggers breakpoint
                            {
                                Name = 'XPS-Viewer'  # does NOT trigger breakpoint
                                Ensure = 'Present'   # does NOT trigger breakpoint
                            }
                        }
    
                        return # does NOT trigger breakpoint
    
                        $doesNotExecute = $true   # Triggers breakpoint
                    }
    
                    $line3 = $true   # Triggers breakpoint
    
                    return   # does NOT trigger breakpoint
    
                    $doesnotexecute = $true   # Triggers breakpoint
    '@
    
                $testState = New-PesterState -Path $root
    
                Enter-CoverageAnalysis -CodeCoverage "$root\TestScriptWithConfiguration.ps1" -PesterState $testState
    
                It 'Has the proper number of breakpoints defined' {
                    $testState.CommandCoverage.Count | Should Be 7
                }
    
                $null = . "$root\TestScriptWithConfiguration.ps1"
    
                $coverageReport = Get-CoverageReport -PesterState $testState
                It 'Reports the proper number of missed commands before running the configuration' {
                    $coverageReport.MissedCommands.Count | Should Be 4
                }
    
                MyTestConfig -OutputPath $root
    
                $coverageReport = Get-CoverageReport -PesterState $testState
                It 'Reports the proper number of missed commands after running the configuration' {
                    $coverageReport.MissedCommands.Count | Should Be 2
                }
    
                Exit-CoverageAnalysis -PesterState $testState
            }
        }
    }
    
  • tools\Functions\Describe.ps1 Show
    function Describe {
    <#
    .SYNOPSIS
    Creates a logical group of tests.  All Mocks and TestDrive contents
    defined within a Describe block are scoped to that Describe; they
    will no longer be present when the Describe block exits.  A Describe
    block may contain any number of Context and It blocks.
    
    .PARAMETER Name
    The name of the test group. This is often an expressive phrase describing the scenario being tested.
    
    .PARAMETER Fixture
    The actual test script. If you are following the AAA pattern (Arrange-Act-Assert), this
    typically holds the arrange and act sections. The Asserts will also lie in this block but are
    typically nested each in its own It block. Assertions are typically performed by the Should
    command within the It blocks.
    
    .PARAMETER Tags
    Optional parameter containing an array of strings.  When calling Invoke-Pester, it is possible to
    specify a -Tag parameter which will only execute Describe blocks containing the same Tag.
    
    .EXAMPLE
    function Add-Numbers($a, $b) {
        return $a + $b
    }
    
    Describe "Add-Numbers" {
        It "adds positive numbers" {
            $sum = Add-Numbers 2 3
            $sum | Should Be 5
        }
    
        It "adds negative numbers" {
            $sum = Add-Numbers (-2) (-2)
            $sum | Should Be (-4)
        }
    
        It "adds one negative number to positive number" {
            $sum = Add-Numbers (-2) 2
            $sum | Should Be 0
        }
    
        It "concatenates strings if given strings" {
            $sum = Add-Numbers two three
            $sum | Should Be "twothree"
        }
    }
    
    .LINK
    It
    Context
    Invoke-Pester
    about_Should
    about_Mocking
    about_TestDrive
    
    #>
    
        param(
            [Parameter(Mandatory = $true, Position = 0)]
            [string] $Name,
            [email protected](),
            [Parameter(Position = 1)]
            [ValidateNotNull()]
            [ScriptBlock] $Fixture = $(Throw "No test script block is provided. (Have you put the open curly brace on the next line?)")
        )
    
        if ($null -eq (Get-Variable -Name Pester -ValueOnly -ErrorAction (Get-IgnoreErrorPreference)))
        {
            # User has executed a test script directly instead of calling Invoke-Pester
            $Pester = New-PesterState -Path (Resolve-Path .) -TestNameFilter $null -TagFilter @() -SessionState $PSCmdlet.SessionState
            $script:mockTable = @{}
        }
    
        if($Pester.TestNameFilter -and ($Name -notlike $Pester.TestNameFilter))
        {
            #skip this test
            return
        }
    
        #TODO add test to test tags functionality
        if($Pester.TagFilter -and @(Compare-Object $Tags $Pester.TagFilter -IncludeEqual -ExcludeDifferent).count -eq 0) {return}
        if($Pester.ExcludeTagFilter -and @(Compare-Object $Tags $Pester.ExcludeTagFilter -IncludeEqual -ExcludeDifferent).count -gt 0) {return}
    
        $Pester.EnterDescribe($Name)
    
        $Pester.CurrentDescribe | Write-Describe
        New-TestDrive
    
        try
        {
            Add-SetupAndTeardown -ScriptBlock $Fixture
            Invoke-TestGroupSetupBlocks -Scope $pester.Scope
            $null = & $Fixture
        }
        catch
        {
            $firstStackTraceLine = $_.InvocationInfo.PositionMessage.Trim() -split '\r?\n' | Select-Object -First 1
            $Pester.AddTestResult('Error occurred in Describe block', "Failed", $null, $_.Exception.Message, $firstStackTraceLine)
            $Pester.TestResult[-1] | Write-PesterResult
        }
        finally
        {
            Invoke-TestGroupTeardownBlocks -Scope $pester.Scope
        }
    
        Clear-SetupAndTeardown
        Remove-TestDrive
        Exit-MockScope
        $Pester.LeaveDescribe()
    }
    
    function Assert-DescribeInProgress
    {
        param ($CommandName)
        if ($null -eq $Pester -or [string]::IsNullOrEmpty($Pester.CurrentDescribe))
        {
            throw "The $CommandName command may only be used inside a Describe block."
        }
    }
    
  • tools\en-US\about_Pester.help.txt Show
    TOPIC
    	Pester
    
    SYNOPSIS
    	Pester is a BDD based test runner for PowerShell.
    
    DESCRIPTION
    	Pester provides a framework for running Unit Tests to execute and validate 
    	Powershell commands. Pester follows a file naming convention for naming 
    	tests to be discovered by pester at test time and a simple set of 
    	functions that expose a Testing DSL for isolating, running, evaluating and 
    	reporting the results of Powershell commands.
    
    	Pester tests can execute any command or script that is accesible to a 
    	pester test file. This can include functions, Cmdlets, Modules and scripts. 
    	Pester can be run in ad hoc style in a console or it can be integrated into 
    	the Build scripts of a Continuous Integration system.
    
    	Pester also contains a powerful set of Mocking Functions that allow tests to 
    	mimic and mock the functionality of any command inside of a piece of 
    	powershell code being tested. See about_Mocking.
    
    CREATING A PESTER TEST
    	To start using Pester, You may use the Add-Fixture function to scaffold both 
    	a new implementation function and a test function.
    
    	C:\PS>New-Fixture deploy Clean
    
    	Creates two files:
    	./deploy/Clean.ps1
    	function clean {
    
    	}
    
        ./deploy/clean.Tests.ps1
        $here = Split-Path -Parent $MyInvocation.MyCommand.Path
        $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
        . "$here\$sut"
    
        Describe "clean" {
    
            It "does something useful" {
                $true | should be $false
            }
        }
    
        Now you have a skeleton of a clean function with a failing test. Pester 
        considers all files containing *Tests.ps1 to be a test file (see 
        Invoke-Pester) and by default it will look for these files and run all 
        Describe blocks inside the file (See Describe). The Describe block can 
        contain several behavior validations expressed in It blocks (see It). 
        Each It block should test one thing and throw an exception if the test 
        fails. Pester will consider any It block that throws an exception to be a 
        failed test. Pester provides a set of extensions that can perform various 
        comparisons between the values emited or altered by a test and an expected 
        value (see about_Should). 
    
    RUNNNING A PESTER TEST
    	Once you have some logic that you are ready to test, run the Tests file directly, 
    	usually by pressing F5 in your ISE. 
    	
    	To run multiple test files, get summary for the test run, to get nUnit compatible XML
    	report or to get PesterResult object use the Invoke-Pester command. You can zero in on 
    	just one test (Describe block) or an entire tree of directories.
    
    	function BuildIfChanged {
    		$thisVersion=Get-Version
    		$nextVersion=Get-NextVersion
    		if($thisVersion -ne $nextVersion) {Build $nextVersion}
    		return $nextVersion
    	}
    
    	$here = Split-Path -Parent $MyInvocation.MyCommand.Path
        $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
        . "$here\$sut"
    
        Describe "BuildIfChanged" {
        	Context "When there are Changes" {
        		Mock Get-Version {return 1.1}
        		Mock Get-NextVersion {return 1.2}
        		Mock Build {} -Verifiable -ParameterFilter {$version -eq 1.2}
    
        		$result = BuildIfChanged
    
    	        It "Builds the next version" {
    	            Assert-VerifiableMocks
    	        }
    	        It "returns the next version number" {
    	            $result | Should Be 1.2
    	        }
            }
        	Context "When there are no Changes" {
        		Mock Get-Version -MockWith {return 1.1}
        		Mock Get-NextVersion -MockWith {return 1.1}
        		Mock Build {}
    
        		$result = BuildIfChanged
    
    	        It "Should not build the next version" {
    	            Assert-MockCalled Build -Times 0 -ParameterFilter{$version -eq 1.1}
    	        }
            }
        }
    
    	C:\PS>Invoke-Pester
    
    	This will run all tests recursively from the current directory downwards 
    	and print a report of all failing and passing tests to the console.
    
    PESTER AND CONTINUOUS INTEGRATION
    	Pester integrates well with almost any build automation solution. You 
    	could create a MSBuild target that calls Pester's convenience Batch file:
    
    	<Target Name="Tests">
    	<Exec Command="cmd /c $(baseDir)pester\bin\pester.bat" />
    	</Target>
    
    	This will start a powershell session, import the Pester Module and call 
    	invoke pester within the current directory. If any test fails, it will 
    	return an exit code equal to the number of failed tests and all test 
    	results will be saved to Test.xml using NUnit's Schema allowing you to 
    	plug these results nicely into most Build systems like CruiseControl, 
    	TeamCity, TFS or Jenkins.
    
    OTHER EXAMPLES
    	Pester's own tests. See all files in the Pester Functions folder 
    	containing *Tests.ps1
    	
    	Chocolatey tests. Chocolatey is a popular powershell based Windows 
    	package management system. It uses Pester tests to validate its own 
    	functionality.
    
    SEE ALSO
    	about_Mocking
    	Describe
    	Context
    	It
    	Add-Fixture
    	Invoke-Pester
    	about_Should
    	about_TestDrive
    
  • tools\Functions\In.ps1 Show
    function In {
    <#
    .SYNOPSIS
    A convenience function that executes a scrit from a specified path.
    
    .DESCRIPTION
    Before the script block passed to the execute parameter is invoked,
    the current location is set to the path specified. Once the script
    block has been executed, the location will be reset to the location
    the script was in prior to calling In.
    
    .PARAMETER Path
    The path that the execute block will be executed in.
    
    .PARAMETER execute
    The script to be executed in the path provided.
    
    #>
    
    param(
        $path,
        [ScriptBlock] $execute
    )
        Assert-DescribeInProgress -CommandName In
    
        $old_pwd = $pwd
        pushd $path
        $pwd = $path
        try {
            & $execute
        } finally {
            popd
            $pwd = $old_pwd
        }
    }
    
  • tools\Functions\In.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "the In statement" {
            Setup -Dir "test_path"
    
            It "executes a command in that directory" {
                In "$TestDrive" -Execute { "" | Out-File "test_file" }
                "$TestDrive\test_file" | Should Exist
            }
    
            It "updates the `$pwd variable when executed" {
                In "$TestDrive\test_path" -Execute { $env:Pester_Test=$pwd }
                $env:Pester_Test | Should Match "test_path"
                $env:Pester_Test=""
            }
        }
    }
    
  • tools\en-US\about_Mocking.help.txt Show
    TOPIC
    	Mocking
    
    SYNOPSIS
    	Pester provides a set of Mocking functions making it easy to fake dependencies 
    	and also to verify behavior. Using these mocking functions can allow you to 
    	"shim" a data layer or mock other complex functions that already have their 
    	own tests.
    
    DESCRIPTION
    	With the set of Mocking functions that Pester exposes, one can:
    
    	- Mock the behavior of ANY powershell command.
    	- Verify that specific commands were (or were not) called.
    	- Verify the number of times a command was called with a set of specified 
    	 parameters.
    
    MOCKING FUNCTIONS
    	See Get-Help for any of the below functions for more detailed information.
    
    	Mock
    		Mocks the behavior of an existing command with an alternate 
    		implementation.
    
    	Assert-VerifiableMocks
    		Checks if any Verifiable Mock has not been invoked. If so, this will 
    		throw an exception.
    
    	Assert-MockCalled
    		Checks if a Mocked command has been called a certain number of times 
    		and throws an exception if it has not.
    
    EXAMPLE
      function Build ($version) {
        Write-Host "a build was run for version: $version"
      }
    
      function BuildIfChanged {
        $thisVersion = Get-Version
        $nextVersion = Get-NextVersion
        if ($thisVersion -ne $nextVersion) { Build $nextVersion }
        return $nextVersion
      }
    
      $here = Split-Path -Parent $MyInvocation.MyCommand.Path
      $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
      . "$here\$sut"
    
      Describe "BuildIfChanged" {
        Context "When there are Changes" {
          Mock Get-Version {return 1.1}
          Mock Get-NextVersion {return 1.2}
          Mock Build {} -Verifiable -ParameterFilter {$version -eq 1.2}
    
          $result = BuildIfChanged
    
          It "Builds the next version" {
            Assert-VerifiableMocks
          }
          It "returns the next version number" {
            $result | Should Be 1.2
          }
        }
        Context "When there are no Changes" {
          Mock Get-Version { return 1.1 }
          Mock Get-NextVersion { return 1.1 }
          Mock Build {}
    
          $result = BuildIfChanged
    
          It "Should not build the next version" {
            Assert-MockCalled Build -Times 0 -ParameterFilter {$version -eq 1.1}
          }
        }
      }
    
    MOCKING CALLS TO COMMANDS MADE FROM INSIDE SCRIPT MODULES
    
    Let's say you have code like this inside a script module (.psm1 file):
    
      function BuildIfChanged {
        $thisVersion = Get-Version
        $nextVersion = Get-NextVersion
        if ($thisVersion -ne $nextVersion) { Build $nextVersion }
        return $nextVersion
      }
    
      function Build ($version) {
        Write-Host "a build was run for version: $version"
      }
    
      # Actual definitions of Get-Version and Get-NextVersion are not shown here,
      # since we'll just be mocking them anyway. However, the commands do need to
      # exist in order to be mocked, so we'll stick dummy functions here
    
      function Get-Version { return 0 }
      function Get-NextVersion { return 0 }
    
      Export-ModuleMember -Function BuildIfChanged
    
    You wish to write a unit test for this module which mocks the calls to Get-Version
    and Get-NextVersion from the module's BuildIfChanged command. In older versions of
    Pester, this was not possible. As of version 3.0, there are two ways you can perform
    unit tests of PowerShell script modules. The first is to inject mocks into a module:
    
    For these example, we'll assume that the PSM1 file is named "MyModule.psm1", and that
    it is installed on your PSModulePath.
    
      Import-Module MyModule
    
      Describe "BuildIfChanged" {
        Context "When there are Changes" {
          Mock -ModuleName MyModule Get-Version { return 1.1 }
          Mock -ModuleName MyModule Get-NextVersion { return 1.2 }
    
          # Just for giggles, we'll also mock Write-Host here, to demonstrate that you can
          # mock calls to commands other than functions defined within the same module.
          Mock -ModuleName MyModule Write-Host {} -Verifiable -ParameterFilter {
            $Object -eq 'a build was run for version: 1.2'
          }
    
          $result = BuildIfChanged
    
          It "Builds the next version and calls Write-Host" {
            Assert-VerifiableMocks
          }
    
          It "returns the next version number" {
            $result | Should Be 1.2
          }
        }
    
        Context "When there are no Changes" {
          Mock -ModuleName MyModule Get-Version { return 1.1 }
          Mock -ModuleName MyModule Get-NextVersion { return 1.1 }
          Mock -ModuleName MyModule Build { }
    
          $result = BuildIfChanged
    
          It "Should not build the next version" {
            Assert-MockCalled Build -ModuleName MyModule -Times 0 -ParameterFilter {
              $version -eq 1.1
            }
          }
        }
      }
    
    Notice that in this example test script, all calls to Mock and Assert-MockCalled have had the
    -ModuleName MyModule parameter added. This tells Pester to inject the mock into the module's scope,
    which causes any calls to those commands from inside the module to execute the mock instead.
    
    When you write your test script this way, you can mock commands that are called by the module's
    internal functions. However, your test script is still limited to accessing the public, exported
    members of the module. If you wanted to write a unit test that calls Build directly, for example,
    it wouldn't work using the above technique. That's where the second approach to script module testing
    comes into play. With Pester 3.0's InModuleScope command, you can cause entire sections of your test
    script to execute inside the targeted script module. This gives you access to non-exported members of
    the module. For example:
    
      Import-Module MyModule
    
      Describe "Unit testing the module's internal Build function:" {
        InModuleScope MyModule {
          $testVersion = 5.0
          Mock Write-Host { }
    
          Build $testVersion
    
          It 'Outputs the correct message' {
            Assert-MockCalled Write-Host -ParameterFilter {
              $Object -eq "a build was run for version: $testVersion"
            }
          }
        }
      }
    
    Notice that when using InModuleScope, you no longer need to specify a -ModuleName parameter when calling
    Mock or Assert-MockCalled for commands within that module. You are also able to directly call the Build
    function, which the module does not export.
    
    SEE ALSO
    	Mock
    	Assert-VerifiableMocks
    	Assert-MockCalled
        InModuleScope
    	Describe
    	Context
    	It
    
  • tools\en-US\about_BeforeEach_AfterEach.help.txt Show
    BeforeEach, AfterEach, BeforeAll, and AfterAll
    -------------------------------
    
    The BeforeEach and AfterEach commands allow you to define setup and teardown tasks that are
    performed at the beginning and end of every It block. This can eliminate duplication of code
    in test scripts, ensure that each test is performed on a pristine state regardless of their
    order, and perform any necessary cleanup tasks after each test.
    
    BeforeEach and AfterEach blocks may be defined inside of any Describe or Context. If they
    are present in both a Context and its parent Describe, BeforeEach blocks in the Describe scope
    are executed first, followed by BeforeEach blocks in the Context scope. AfterEach blocks are
    the reverse of this, with the Context  AfterEach blocks executing before Describe.
    
    The script blocks assigned to BeforeEach and AfterEach are dot-sourced in the Context or Describe
    which contains the current It statement, so you don't have to worry about the scope of variable
    assignments. Any variables that are assigned values within a BeforeEach block can be used inside
    the body of the It block.
    
    BeforeAll and AfterAll are used the same way as BeforeEach and AfterEach, except that they are
    executed at the beginning and end of their containing Describe or Context block.  This is
    essentially syntactic sugar for the following arrangement of code:
    
    Describe 'Something' {
        try
        {
            <BeforeAll Code Here>
    
            <Describe Body>
        }
        finally
        {
            <AfterAll Code Here>
        }
    }
    
    Note about syntax and placement
    -------------------------------
    
    Unlike most of the commands in a Pester script, BeforeEach, AfterEach, BeforeAll and AfterAll blocks
    apply to the entire Describe or Context scope in which they are defined, regardless of the order of
    commands inside the Describe or Context. In other words, even if an It block appears before BeforeEach
    or AfterEach in the tests file, the BeforeEach and AfterEach will still be executed.  Likewise, BeforeAll
    code will be executed at the beginning of a Context or Describe block regardless of where it is found,
    and AfterAll code will execute at the end of the Context or Describe.
    
    Examples
    -------------------------------
    
    Describe 'Testing BeforeEach and AfterEach' {
        $afterEachVariable = 'AfterEach has not been executed yet'
    
        It 'Demonstrates that BeforeEach may be defined after the It command' {
            $beforeEachVariable | Should Be 'Set in a describe-scoped BeforeEach'
            $afterEachVariable  | Should Be 'AfterEach has not been executed yet'
            $beforeAllVariable  | Should Be 'BeforeAll has been executed'
        }
    
        It 'Demonstrates that AfterEach has executed after the end of the first test' {
            $afterEachVariable | Should Be 'AfterEach has been executed'
        }
    
        BeforeEach {
            $beforeEachVariable = 'Set in a describe-scoped BeforeEach'
        }
    
        AfterEach {
            $afterEachVariable = 'AfterEach has been executed'
        }
    
        BeforeAll {
            $beforeAllVariable = 'BeforeAll has been executed'
        }
    }
    
  • tools\Functions\Assertions\MatchExactly.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "MatchExactly" {
            It "returns true for things that match exactly" {
                PesterMatchExactly "foobar" "ob" | Should Be $true
            }
    
            It "returns false for things that do not match exactly" {
                PesterMatchExactly "foobar" "FOOBAR" | Should Be $false
            }
    
            It "uses regular expressions" {
                PesterMatchExactly "foobar" "\S{6}" | Should Be $true
            }
        }
    }
    
  • tools\Functions\Assertions\PesterThrow.ps1 Show
    $ActualExceptionMessage = ""
    $ActualExceptionWasThrown = $false
    
    # because this is a script block, the user will have to
    # wrap the code they want to assert on in { }
    function PesterThrow([scriptblock] $script, $expectedErrorMessage) {
        $Script:ActualExceptionMessage = ""
        $Script:ActualExceptionWasThrown = $false
    
        try {
            # Redirect to $null so script output does not enter the pipeline
            & $script > $null
        } catch {
            $Script:ActualExceptionWasThrown = $true
            $Script:ActualExceptionMessage = $_.Exception.Message
            $Script:ActualExceptionLine = Get-ExceptionLineInfo $_.InvocationInfo
        }
    
        if ($ActualExceptionWasThrown) {
            return Get-DoMessagesMatch $ActualExceptionMessage $expectedErrorMessage
        }
        return $false
    }
    
    function Get-DoMessagesMatch($value, $expected) {
        if ($expected -eq "") { return $false }
        return $value.Contains($expected)
    }
    
    function Get-ExceptionLineInfo($info) {
        # $info.PositionMessage has a leading blank line that we need to account for in PowerShell 2.0
        $positionMessage = $info.PositionMessage -split '\r?\n' -match '\S' -join "`r`n"
        return ($positionMessage -replace "^At ","from ")
    }
    
    function PesterThrowFailureMessage($value, $expected) {
        if ($expected) {
            return "Expected: the expression to throw an exception with message {{{0}}}, an exception was {2}raised, message was {{{1}}}`n    {3}" -f
                   $expected, $ActualExceptionMessage,(@{$true="";$false="not "}[$ActualExceptionWasThrown]),($ActualExceptionLine  -replace "`n","`n    ")
        } else {
          return "Expected: the expression to throw an exception"
        }
    }
    
    function NotPesterThrowFailureMessage($value, $expected) {
        if ($expected) {
            return "Expected: the expression not to throw an exception with message {{{0}}}, an exception was {2}raised, message was {{{1}}}`n    {3}" -f
                   $expected, $ActualExceptionMessage,(@{$true="";$false="not "}[$ActualExceptionWasThrown]),($ActualExceptionLine  -replace "`n","`n    ")
        } else {
            return "Expected: the expression not to throw an exception. Message was {{{0}}}`n    {1}" -f $ActualExceptionMessage,($ActualExceptionLine  -replace "`n","`n    ")
        }
    }
    
  • tools\Functions\Assertions\PesterThrow.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterThrow" {
            It "returns true if the statement throws an exception" {
                Test-PositiveAssertion (PesterThrow { throw })
            }
    
            It "returns false if the statement does not throw an exception" {
                Test-NegativeAssertion (PesterThrow { 1 + 1 })
            }
    
            It "returns true if the statement throws an exception and the actual error text matches the expected error text" {
                $expectedErrorMessage = "expected error message"
                Test-PositiveAssertion (PesterThrow { throw $expectedErrorMessage } $expectedErrorMessage)
            }
    
            It "returns false if the statement throws an exception and the actual error does not match the expected error text" {
                $unexpectedErrorMessage = "unexpected error message"
                $expectedErrorMessage = "some expected error message"
                Test-NegativeAssertion (PesterThrow { throw $unexpectedErrorMessage} $expectedErrorMessage)
            }
    
            It "returns true if the statement throws an exception and the actual error text matches the expected error pattern" {
                Test-PositiveAssertion (PesterThrow { throw "expected error"} "error")
            }
        }
    
        Describe "Get-DoMessagesMatch" {
            It "returns true if the actual message is the same as the expected message" {
                $expectedErrorMessage = "expected"
                $actualErrorMesage = "expected"
                $result = Get-DoMessagesMatch $actualErrorMesage $expectedErrorMessage
                $result | Should Be $True
            }
    
            It "returns false if the actual message is not the same as the expected message" {
                $expectedErrorMessage = "some expected message"
                $actualErrorMesage = "unexpected"
                $result = Get-DoMessagesMatch $actualErrorMesage $expectedErrorMessage
                $result | Should Be $False
            }
    
            It "returns false is there's no expectation" {
                $result = Get-DoMessagesMatch "" ""
                $result | Should Be $False
            }
    
            It "returns true if the expected error is contained in the actual message" {
                $actualErrorMesage = "this is a long error message"
                $expectedText = "long error"
                $result = Get-DoMessagesMatch $actualErrorMesage $expectedText
                $result | Should Be $True
            }
        }
    
        Describe 'PesterThrowFailureMessage' {
            $testScriptPath = Join-Path $TestDrive.FullName test.ps1
    
            It 'returns false if the actual message is not the same as the expected message' {
                $unexpectedErrorMessage = 'unexpected'
                $expectedErrorMessage = 'some expected message'
                Set-Content -Path $testScriptPath -Value "throw '$unexpectedErrorMessage'"
    
                PesterThrow { & $testScriptPath } $expectedErrorMessage > $null
                $result = PesterThrowFailureMessage $unexpectedErrorMessage $expectedErrorMessage
                $result | Should Match "^Expected: the expression to throw an exception with message {$expectedErrorMessage}, an exception was raised, message was {$unexpectedErrorMessage}`n    from $([RegEx]::Escape($testScriptPath)):\d+ char:\d+"
            }
    
            It 'returns true if the actual message is the same as the expected message' {
                PesterThrow { } > $null
                $result = PesterThrowFailureMessage 'error message'
                $result | Should Be 'Expected: the expression to throw an exception'
            }
        }
    
        Describe 'NotPesterThrowFailureMessage' {
            $testScriptPath = Join-Path $TestDrive.FullName test.ps1
    
            It 'returns false if the actual message is not the same as the expected message' {
                $unexpectedErrorMessage = 'unexpected'
                $expectedErrorMessage = 'some expected message'
                Set-Content -Path $testScriptPath -Value "throw '$unexpectedErrorMessage'"
    
                PesterThrow { & $testScriptPath } $expectedErrorMessage > $null
                $result = NotPesterThrowFailureMessage $unexpectedErrorMessage $expectedErrorMessage
                $result | Should Match "^Expected: the expression not to throw an exception with message {$expectedErrorMessage}, an exception was raised, message was {$unexpectedErrorMessage}`n    from $([RegEx]::Escape($testScriptPath)):\d+ char:\d+"
            }
    
            It 'returns true if the actual message is the same as the expected message' {
                Set-Content -Path $testScriptPath -Value "throw 'error message'"
                PesterThrow { & $testScriptPath } > $null
                $result = NotPesterThrowFailureMessage 'error message'
                $result | Should Match "^Expected: the expression not to throw an exception. Message was {error message}`n    from $([RegEx]::Escape($testScriptPath)):\d+ char:\d+"
            }
        }
    }
    
  • tools\Functions\Mock.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    function FunctionUnderTest
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory=$false)]
            [string]
            $param1
        )
    
        return "I am a real world test"
    }
    
    function FunctionUnderTestWithoutParams([string]$param1) {
        return "I am a real world test with no params"
    }
    
    filter FilterUnderTest { $_ }
    
    function CommonParamFunction (
        [string] ${Uncommon},
        [switch]
        ${Verbose},
        [switch]
        ${Debug},
        [System.Management.Automation.ActionPreference]
        ${ErrorAction},
        [System.Management.Automation.ActionPreference]
        ${WarningAction},
        [System.String]
        ${ErrorVariable},
        [System.String]
        ${WarningVariable},
        [System.String]
        ${OutVariable},
        [System.Int32]
        ${OutBuffer} ){
        return "Please strip me of my common parameters. They are far too common."
    }
    
    Describe "When calling Mock on existing function" {
        Mock FunctionUnderTest { return "I am the mock test that was passed $param1"}
    
        $result = FunctionUnderTest "boundArg"
    
        It "Should rename function under test" {
            $renamed = (Test-Path function:PesterIsMocking_FunctionUnderTest)
            $renamed | Should Be $true
        }
    
        It "Should Invoke the mocked script" {
            $result | Should Be "I am the mock test that was passed boundArg"
        }
    }
    
    Describe "When the caller mocks a command Pester uses internally" {
        Mock Write-Host { }
    
        Context "Context run when Write-Host is mocked" {
            It "does not make extra calls to the mocked command" {
                Write-Host 'Some String'
                Assert-MockCalled 'Write-Host' -Exactly 1
            }
    
            It "retains the correct mock count after the first test completes" {
                Assert-MockCalled 'Write-Host' -Exactly 1
            }
        }
    }
    
    Describe "When calling Mock on existing cmdlet" {
        Mock Get-Process {return "I am not Get-Process"}
    
        $result=Get-Process
    
        It "Should Invoke the mocked script" {
            $result | Should Be "I am not Get-Process"
        }
    
        It 'Should not resolve $args to the parent scope' {
            { $args = 'From', 'Parent', 'Scope'; Get-Process SomeName } | Should Not Throw
        }
    }
    
    Describe 'When calling Mock on an alias' {
        Mock dir {return 'I am not dir'}
    
        $result = dir
    
        It 'Should Invoke the mocked script' {
            $result | Should Be 'I am not dir'
        }
    }
    
    Describe 'When calling Mock on a filter' {
        Mock FilterUnderTest {return 'I am not FilterUnderTest'}
    
        $result = 'Yes I am' | FilterUnderTest
    
        It 'Should Invoke the mocked script' {
            $result | Should Be 'I am not FilterUnderTest'
        }
    }
    
    Describe 'When calling Mock on an external script' {
        $ps1File = New-Item 'TestDrive:\tempExternalScript.ps1' -ItemType File -Force
        $ps1File | Set-Content -Value "'I am tempExternalScript.ps1'"
    
        Mock 'TestDrive:\tempExternalScript.ps1' {return 'I am not tempExternalScript.ps1'}
    
        <#
            # Invoking the script using its absolute path is not supported
    
            $result = TestDrive:\tempExternalScript.ps1
            It 'Should Invoke the absolute-path-qualified mocked script using just the script name' {
                $result | Should Be 'I am not tempExternalScript.ps1'
            }
    
            $result = & TestDrive:\tempExternalScript.ps1
            It 'Should Invoke the absolute-path-qualified mocked script using the command-invocation operator (&)' {
                $result | Should Be 'I am not tempExternalScript.ps1'
            }
    
            $result = . TestDrive:\tempExternalScript.ps1
            It 'Should Invoke the absolute-path-qualified mocked script using dot source notation' {
                $result | Should Be 'I am not tempExternalScript.ps1'
            }
        #>
    
        Push-Location TestDrive:\
    
        try
        {
            $result = tempExternalScript.ps1
            It 'Should Invoke the mocked script using just the script name' {
                $result | Should Be 'I am not tempExternalScript.ps1'
            }
    
            $result = & tempExternalScript.ps1
            It 'Should Invoke the mocked script using the command-invocation operator' {
                #the command invocation operator is (&). Moved this to comment because it breaks the contionuous builds.
                #there is issue for this on GH
    
                $result | Should Be 'I am not tempExternalScript.ps1'
            }
    
            $result = . tempExternalScript.ps1
            It 'Should Invoke the mocked script using dot source notation' {
                $result | Should Be 'I am not tempExternalScript.ps1'
            }
    
            <#
                # Invoking the script using only its relative path is not supported
    
                $result = .\tempExternalScript.ps1
                It 'Should Invoke the relative-path-qualified mocked script' {
                    $result | Should Be 'I am not tempExternalScript.ps1'
                }
            #>
    
        }
        finally
        {
            Pop-Location
        }
    
        Remove-Item $ps1File -Force -ErrorAction SilentlyContinue
    }
    
    Describe 'When calling Mock on an application command' {
        Mock schtasks.exe {return 'I am not schtasks.exe'}
    
        $result = schtasks.exe
    
        It 'Should Invoke the mocked script' {
            $result | Should Be 'I am not schtasks.exe'
        }
    }
    
    Describe "When calling Mock in the Describe block" {
        Mock Out-File {return "I am not Out-File"}
    
        It "Should mock Out-File successfully" {
            $outfile = "test" | Out-File "TestDrive:\testfile.txt"
            $outfile | Should Be "I am not Out-File"
        }
    }
    
    Describe "When calling Mock on existing cmdlet to handle pipelined input" {
        Mock Get-ChildItem {
            if($_ -eq 'a'){
                return "AA"
            }
            if($_ -eq 'b'){
                return "BB"
            }
        }
    
        $result = ''
        "a", "b" | Get-ChildItem | % { $result += $_ }
    
        It "Should process the pipeline in the mocked script" {
            $result | Should Be "AABB"
        }
    }
    
    Describe "When calling Mock on existing cmdlet with Common params" {
        Mock CommonParamFunction
    
        $result=[string](Get-Content function:\CommonParamFunction)
    
        It "Should strip verbose" {
            $result.contains("`${Verbose}") | Should Be $false
        }
        It "Should strip Debug" {
            $result.contains("`${Debug}") | Should Be $false
        }
        It "Should strip ErrorAction" {
            $result.contains("`${ErrorAction}") | Should Be $false
        }
        It "Should strip WarningAction" {
            $result.contains("`${WarningAction}") | Should Be $false
        }
        It "Should strip ErrorVariable" {
            $result.contains("`${ErrorVariable}") | Should Be $false
        }
        It "Should strip WarningVariable" {
            $result.contains("`${WarningVariable}") | Should Be $false
        }
        It "Should strip OutVariable" {
            $result.contains("`${OutVariable}") | Should Be $false
        }
        It "Should strip OutBuffer" {
            $result.contains("`${OutBuffer}") | Should Be $false
        }
        It "Should not strip an Uncommon param" {
            $result.contains("`${Uncommon}") | Should Be $true
        }
    }
    
    Describe "When calling Mock on non-existing function" {
        try{
            Mock NotFunctionUnderTest {return}
        } Catch {
            $result=$_
        }
    
        It "Should throw correct error" {
            $result.Exception.Message | Should Be "Could not find command NotFunctionUnderTest"
        }
    }
    
    Describe 'When calling Mock, StrictMode is enabled, and variables are used in the ParameterFilter' {
        Set-StrictMode -Version Latest
    
        $result = $null
        $testValue = 'test'
    
        try
        {
            Mock FunctionUnderTest { 'I am the mock' } -ParameterFilter { $param1 -eq $testValue }
        }
        catch
        {
            $result = $_
        }
    
        It 'Does not throw an error when testing the parameter filter' {
            $result | Should Be $null
        }
    
        It 'Calls the mock properly' {
            FunctionUnderTest $testValue | Should Be 'I am the mock'
        }
    
        It 'Properly asserts the mock was called when there is a variable in the parameter filter' {
            Assert-MockCalled FunctionUnderTest -Exactly 1 -ParameterFilter { $param1 -eq $testValue }
        }
    }
    
    Describe "When calling Mock on existing function without matching bound params" {
        Mock FunctionUnderTest {return "fake results"} -parameterFilter {$param1 -eq "test"}
    
        $result=FunctionUnderTest "badTest"
    
        It "Should redirect to real function" {
            $result | Should Be "I am a real world test"
        }
    }
    
    Describe "When calling Mock on existing function with matching bound params" {
        Mock FunctionUnderTest {return "fake results"} -parameterFilter {$param1 -eq "badTest"}
    
        $result=FunctionUnderTest "badTest"
    
        It "Should return mocked result" {
            $result | Should Be "fake results"
        }
    }
    
    Describe "When calling Mock on existing function without matching unbound arguments" {
        Mock FunctionUnderTestWithoutParams {return "fake results"} -parameterFilter {$param1 -eq "test" -and $args[0] -eq 'notArg0'}
    
        $result=FunctionUnderTestWithoutParams -param1 "test" "arg0"
    
        It "Should redirect to real function" {
            $result | Should Be "I am a real world test with no params"
        }
    }
    
    Describe "When calling Mock on existing function with matching unbound arguments" {
        Mock FunctionUnderTestWithoutParams {return "fake results"} -parameterFilter {$param1 -eq "badTest" -and $args[0] -eq 'arg0'}
    
        $result=FunctionUnderTestWithoutParams "badTest" "arg0"
    
        It "Should return mocked result" {
            $result | Should Be "fake results"
        }
    }
    
    Describe 'When calling Mock on a function that has no parameters' {
        function Test-Function { }
        Mock Test-Function { return $args.Count }
    
        It 'Sends the $args variable properly with 2+ elements' {
            Test-Function 1 2 3 4 5 | Should Be 5
        }
    
        It 'Sends the $args variable properly with 1 element' {
            Test-Function 1 | Should Be 1
        }
    
        It 'Sends the $args variable properly with 0 elements' {
            Test-Function | Should Be 0
        }
    }
    
    Describe "When calling Mock on cmdlet Used by Mock" {
        Mock Set-Item {return "I am not Set-Item"}
        Mock Set-Item {return "I am not Set-Item"}
    
        $result = Set-Item "mypath" -value "value"
    
        It "Should Invoke the mocked script" {
            $result | Should Be "I am not Set-Item"
        }
    }
    
    Describe "When calling Mock on More than one command" {
        Mock Invoke-Command {return "I am not Invoke-Command"}
        Mock FunctionUnderTest {return "I am the mock test"}
    
        $result = Invoke-Command {return "yes I am"}
        $result2 = FunctionUnderTest
    
        It "Should Invoke the mocked script for the first Mock" {
            $result | Should Be "I am not Invoke-Command"
        }
        It "Should Invoke the mocked script for the second Mock" {
            $result2 | Should Be "I am the mock test"
        }
    }
    
    Describe 'When calling Mock on a module-internal function.' {
        New-Module -Name TestModule {
            function InternalFunction { 'I am the internal function' }
            function PublicFunction   { InternalFunction }
            function PublicFunctionThatCallsExternalCommand { Start-Sleep 0 }
            Export-ModuleMember -Function PublicFunction, PublicFunctionThatCallsExternalCommand
        } | Import-Module -Force
    
        New-Module -Name TestModule2 {
            function InternalFunction { 'I am the second module internal function' }
            function InternalFunction2 { 'I am the second module, second function' }
            function PublicFunction   { InternalFunction }
            function PublicFunction2 { InternalFunction2 }
            Export-ModuleMember -Function PublicFunction, PublicFunction2
        } | Import-Module -Force
    
        It 'Should fail to call the internal module function' {
            { TestModule\InternalFuncTion } | Should Throw
        }
    
        It 'Should call the actual internal module function from the public function' {
            TestModule\PublicFunction | Should Be 'I am the internal function'
        }
    
        Context 'Using Mock -ModuleName "ModuleName" "CommandName" syntax' {
            Mock -ModuleName TestModule InternalFunction { 'I am the mock test' }
    
            It 'Should call the mocked function' {
                TestModule\PublicFunction | Should Be 'I am the mock test'
            }
    
            Mock -ModuleName TestModule Start-Sleep { }
    
            It 'Should mock calls to external functions from inside the module' {
                PublicFunctionThatCallsExternalCommand
    
                Assert-MockCalled -ModuleName TestModule Start-Sleep -Exactly 1
            }
    
            Mock -ModuleName TestModule2 InternalFunction -ParameterFilter { $args[0] -eq 'Test' } {
                "I'm the mock who's been passed parameter Test"
            }
    
            It 'Should only call mocks within the same module' {
                TestModule2\PublicFunction | Should Be 'I am the second module internal function'
            }
    
            Mock -ModuleName TestModule2 InternalFunction2 {
                InternalFunction 'Test'
            }
    
            It 'Should call mocks from inside another mock' {
                TestModule2\PublicFunction2 | Should Be "I'm the mock who's been passed parameter Test"
            }
        }
    
        Remove-Module TestModule -Force
        Remove-Module TestModule2 -Force
    }
    
    Describe "When Applying multiple Mocks on a single command" {
        Mock FunctionUnderTest {return "I am the first mock test"} -parameterFilter {$param1 -eq "one"}
        Mock FunctionUnderTest {return "I am the Second mock test"} -parameterFilter {$param1 -eq "two"}
    
        $result = FunctionUnderTest "one"
        $result2= FunctionUnderTest "two"
    
        It "Should Invoke the mocked script for the first Mock" {
            $result | Should Be "I am the first mock test"
        }
        It "Should Invoke the mocked script for the second Mock" {
            $result2 | Should Be "I am the Second mock test"
        }
    }
    
    Describe "When Applying multiple Mocks with filters on a single command where both qualify" {
        Mock FunctionUnderTest {return "I am the first mock test"} -parameterFilter {$param1.Length -gt 0 }
        Mock FunctionUnderTest {return "I am the Second mock test"} -parameterFilter {$param1 -gt 1 }
    
        $result = FunctionUnderTest "one"
    
        It "The last Mock should win" {
            $result | Should Be "I am the Second mock test"
        }
    }
    
    Describe "When Applying multiple Mocks on a single command where one has no filter" {
        Mock FunctionUnderTest {return "I am the first mock test"} -parameterFilter {$param1 -eq "one"}
        Mock FunctionUnderTest {return "I am the paramless mock test"}
        Mock FunctionUnderTest {return "I am the Second mock test"} -parameterFilter {$param1 -eq "two"}
    
        $result = FunctionUnderTest "one"
        $result2= FunctionUnderTest "three"
    
        It "The parameterless mock is evaluated last" {
            $result | Should Be "I am the first mock test"
        }
    
        It "The parameterless mock will be applied if no other wins" {
            $result2 | Should Be "I am the paramless mock test"
        }
    }
    
    Describe "When Creating a Verifiable Mock that is not called" {
        Context "In the test script's scope" {
            Mock FunctionUnderTest {return "I am a verifiable test"} -Verifiable -parameterFilter {$param1 -eq "one"}
            FunctionUnderTest "three" | Out-Null
    
            try {
                Assert-VerifiableMocks
            } Catch {
                $result=$_
            }
    
            It "Should throw" {
                $result.Exception.Message | Should Be "`r`n Expected FunctionUnderTest to be called with `$param1 -eq `"one`""
            }
        }
    
        Context "In a module's scope" {
            New-Module -Name TestModule -ScriptBlock {
                function ModuleFunctionUnderTest { return 'I am the function under test in a module' }
            } | Import-Module -Force
    
            Mock -ModuleName TestModule ModuleFunctionUnderTest {return "I am a verifiable test"} -Verifiable -parameterFilter {$param1 -eq "one"}
            TestModule\ModuleFunctionUnderTest "three" | Out-Null
    
            try {
                Assert-VerifiableMocks
            } Catch {
                $result=$_
            }
    
            It "Should throw" {
                $result.Exception.Message | Should Be "`r`n Expected ModuleFunctionUnderTest in module TestModule to be called with `$param1 -eq `"one`""
            }
    
            Remove-Module TestModule -Force
        }
    }
    
    Describe "When Creating a Verifiable Mock that is called" {
        Mock FunctionUnderTest -Verifiable -parameterFilter {$param1 -eq "one"}
        FunctionUnderTest "one"
        It "Assert-VerifiableMocks Should not throw" {
            { Assert-VerifiableMocks } | Should Not Throw
        }
    }
    
    Describe "When Calling Assert-MockCalled 0 without exactly" {
        Mock FunctionUnderTest {}
        FunctionUnderTest "one"
    
        try {
            Assert-MockCalled FunctionUnderTest 0
        } Catch {
            $result=$_
        }
    
        It "Should throw if mock was called" {
            $result.Exception.Message | Should Be "Expected FunctionUnderTest to be called 0 times exactly but was called 1 times"
        }
    
        It "Should not throw if mock was not called" {
            Assert-MockCalled FunctionUnderTest 0 { $param1 -eq "stupid" }
        }
    }
    
    Describe "When Calling Assert-MockCalled with exactly" {
        Mock FunctionUnderTest {}
        FunctionUnderTest "one"
        FunctionUnderTest "one"
    
        try {
            Assert-MockCalled FunctionUnderTest -exactly 3
        } Catch {
            $result=$_
        }
    
        It "Should throw if mock was not called the number of times specified" {
            $result.Exception.Message | Should Be "Expected FunctionUnderTest to be called 3 times exactly but was called 2 times"
        }
    
        It "Should not throw if mock was called the number of times specified" {
            Assert-MockCalled FunctionUnderTest -exactly 2 { $param1 -eq "one" }
        }
    }
    
    Describe "When Calling Assert-MockCalled without exactly" {
        Mock FunctionUnderTest {}
        FunctionUnderTest "one"
        FunctionUnderTest "one"
    
        try {
            Assert-MockCalled FunctionUnderTest 3
        } Catch {
            $result=$_
        }
    
        It "Should throw if mock was not called atleast the number of times specified" {
            $result.Exception.Message | Should Be "Expected FunctionUnderTest to be called at least 3 times but was called 2 times"
        }
    
        It "Should not throw if mock was called at least the number of times specified" {
            Assert-MockCalled FunctionUnderTest
        }
    
        It "Should not throw if mock was called at exactly the number of times specified" {
            Assert-MockCalled FunctionUnderTest 2 { $param1 -eq "one" }
        }
    }
    
    Describe "Using Pester Scopes (Describe,Context,It)" {
        Mock FunctionUnderTest {return "I am the first mock test"} -parameterFilter {$param1 -eq "one"}
        Mock FunctionUnderTest {return "I am the paramless mock test"}
    
        Context "When in the first context" {
            It "should mock Describe scoped paramles mock" {
                FunctionUnderTest | should be "I am the paramless mock test"
            }
            It "should mock Describe scoped single param mock" {
                FunctionUnderTest "one" | should be "I am the first mock test"
            }
        }
    
        Context "When in the second context" {
            It "should mock Describe scoped paramles mock again" {
                FunctionUnderTest | should be "I am the paramless mock test"
            }
            It "should mock Describe scoped single param mock again" {
                FunctionUnderTest "one" | should be "I am the first mock test"
            }
        }
    
        Context "When using mocks in both scopes" {
            Mock FunctionUnderTestWithoutParams {return "I am the other function"}
    
            It "should mock Describe scoped mock." {
                FunctionUnderTest | should be "I am the paramless mock test"
            }
            It "should mock Context scoped mock." {
                FunctionUnderTestWithoutParams | should be "I am the other function"
            }
        }
    
        Context "When context hides a describe mock" {
            Mock FunctionUnderTest {return "I am the context mock"}
            Mock FunctionUnderTest {return "I am the parameterized context mock"} -parameterFilter {$param1 -eq "one"}
    
            It "should use the context paramles mock" {
                FunctionUnderTest | should be "I am the context mock"
            }
            It "should use the context parameterized mock" {
                FunctionUnderTest "one" | should be "I am the parameterized context mock"
            }
        }
    
        Context "When context no longer hides a describe mock" {
            It "should use the describe mock" {
                FunctionUnderTest | should be "I am the paramless mock test"
            }
    
            It "should use the describe parameterized mock" {
                FunctionUnderTest "one" | should be "I am the first mock test"
            }
        }
    
        Context 'When someone calls Mock from inside an It block' {
            Mock FunctionUnderTest { return 'I am the context mock' }
    
            It 'Sets the mock' {
                Mock FunctionUnderTest { return 'I am the It mock' }
            }
    
            It 'Leaves the mock active in the parent scope' {
                FunctionUnderTest | Should Be 'I am the It mock'
            }
        }
    }
    
    Describe 'Testing mock history behavior from each scope' {
        function MockHistoryChecker { }
        Mock MockHistoryChecker { 'I am the describe mock.' }
    
        Context 'Without overriding the mock in lower scopes' {
            It "Reports that zero calls have been made to in the describe scope" {
                Assert-MockCalled MockHistoryChecker -Exactly 0 -Scope Describe
            }
    
            It 'Calls the describe mock' {
                MockHistoryChecker | Should Be 'I am the describe mock.'
            }
    
            It "Reports that zero calls have been made in an It block, after a context-scoped call" {
                Assert-MockCalled MockHistoryChecker -Exactly 0 -Scope It
            }
    
            It "Reports one Context-scoped call" {
                Assert-MockCalled MockHistoryChecker -Exactly 1
            }
    
            It "Reports one Describe-scoped call" {
                Assert-MockCalled MockHistoryChecker -Exactly 1 -Scope Describe
            }
        }
    
        Context 'After exiting the previous context' {
            It 'Reports zero context-scoped calls in the new context.' {
                Assert-MockCalled MockHistoryChecker -Exactly 0
            }
    
            It 'Reports one describe-scoped call from the previous context' {
                Assert-MockCalled MockHistoryChecker -Exactly 1 -Scope Describe
            }
        }
    
        Context 'While overriding mocks in lower scopes' {
            Mock MockHistoryChecker { 'I am the context mock.' }
    
            It 'Calls the context mock' {
                MockHistoryChecker | Should Be 'I am the context mock.'
            }
    
            It 'Reports one context-scoped call' {
                Assert-MockCalled MockHistoryChecker -Exactly 1
            }
    
            It 'Reports two describe-scoped calls, even when one is an override mock in a lower scope' {
                Assert-MockCalled MockHistoryChecker -Exactly 2 -Scope Describe
            }
    
            It 'Calls an It-scoped mock' {
                Mock MockHistoryChecker { 'I am the It mock.' }
                MockHistoryChecker | Should Be 'I am the It mock.'
            }
    
            It 'Reports 2 context-scoped calls' {
                Assert-MockCalled MockHistoryChecker -Exactly 2
            }
    
            It 'Reports 3 describe-scoped calls' {
                Assert-MockCalled MockHistoryChecker -Exactly 3 -Scope Describe
            }
        }
    
        It 'Reports 3 describe-scoped calls using the default scope in a Describe block' {
            Assert-MockCalled MockHistoryChecker -Exactly 3
        }
    }
    
    Describe "Using a single no param Describe" {
        Mock FunctionUnderTest {return "I am the describe mock test"}
    
        Context "With a context mocking the same function with no params"{
            Mock FunctionUnderTest {return "I am the context mock test"}
            It "Should use the context mock" {
                FunctionUnderTest | should be "I am the context mock test"
            }
        }
    }
    
    Describe 'Dot Source Test' {
        # This test is only meaningful if this test file is dot-sourced in the global scope.  If it's executed without
        # dot-sourcing or run by Invoke-Pester, there's no problem.
    
        function TestFunction { Test-Path -Path 'Test' }
        Mock Test-Path { }
    
        $null = TestFunction
    
        It "Calls the mock with parameter 'Test'" {
            Assert-MockCalled Test-Path -Exactly 1 -ParameterFilter { $Path -eq 'Test' }
        }
    
        It "Doesn't call the mock with any other parameters" {
            Assert-MockCalled Test-Path -Exactly 0 -ParameterFilter { $Path -ne 'Test' }
        }
    }
    
    Describe 'Mocking Cmdlets with dynamic parameters' {
        $mockWith = { if (-not $CodeSigningCert) { throw 'CodeSigningCert variable not found, or set to false!' } }
        Mock Get-ChildItem -MockWith $mockWith -ParameterFilter { [bool]$CodeSigningCert }
    
        It 'Allows calls to be made with dynamic parameters (including parameter filters)' {
            { Get-ChildItem -Path Cert:\ -CodeSigningCert } | Should Not Throw
            Assert-MockCalled Get-ChildItem
        }
    }
    
    Describe 'Mocking functions with dynamic parameters' {
    
        # Get-Greeting sample function borrowed and modified from Bartek Bielawski's
        # blog at http://becomelotr.wordpress.com/2012/05/10/using-and-abusing-dynamic-parameters/
    
        function Get-Greeting {
            [CmdletBinding()]
            param (
                $Name
            )
    
            DynamicParam {
                if ($Name -cmatch '\b[a-z]') {
                    $Attributes = New-Object Management.Automation.ParameterAttribute
                    $Attributes.ParameterSetName = "__AllParameterSets"
                    $Attributes.Mandatory = $false
    
                    $AttributeCollection = New-Object Collections.ObjectModel.Collection[Attribute]
                    $AttributeCollection.Add($Attributes)
    
                    $Dynamic = New-Object System.Management.Automation.RuntimeDefinedParameter('Capitalize', [switch], $AttributeCollection)
    
                    $ParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                    $ParamDictionary.Add("Capitalize", $Dynamic)
                    $ParamDictionary
                }
            }
    
            end
            {
                if($PSBoundParameters.Capitalize) {
                    $Name = [regex]::Replace(
                        $Name,
                        '\b\w',
                        { $args[0].Value.ToUpper() }
                    )
                }
    
                "Welcome $Name!"
            }
        }
    
        $mockWith = { if (-not $Capitalize) { throw 'Capitalize variable not found, or set to false!' } }
        Mock Get-Greeting -MockWith $mockWith -ParameterFilter { [bool]$Capitalize }
    
        It 'Allows calls to be made with dynamic parameters (including parameter filters)' {
            { Get-Greeting -Name lowercase -Capitalize } | Should Not Throw
            Assert-MockCalled Get-Greeting
        }
    
        Context 'When a variable with the same name as a dynamic parameter exists in a parent scope' {
            $Capitalize = $false
    
            It 'Still sets the parameter variable properly in the parameter filter and mock body' {
                { Get-Greeting -Name lowercase -Capitalize } | Should Not Throw
                Assert-MockCalled Get-Greeting -Scope It
            }
        }
    }
    
    Describe 'Mocking Cmdlets with dynamic parameters in a module' {
        New-Module -Name TestModule {
            function PublicFunction   { Get-ChildItem -Path Cert:\ -CodeSigningCert }
        } | Import-Module -Force
    
        $mockWith = { if (-not $CodeSigningCert) { throw 'CodeSigningCert variable not found, or set to false!' } }
        Mock Get-ChildItem -MockWith $mockWith -ModuleName TestModule -ParameterFilter { [bool]$CodeSigningCert }
    
        It 'Allows calls to be made with dynamic parameters (including parameter filters)' {
            { TestModule\PublicFunction } | Should Not Throw
            Assert-MockCalled Get-ChildItem -ModuleName TestModule
        }
    
        Remove-Module TestModule -Force
    }
    
    Describe 'Mocking functions with dynamic parameters in a module' {
        New-Module -Name TestModule {
            function PublicFunction { Get-Greeting -Name lowercase -Capitalize }
    
            $script:DoDynamicParam = $true
    
            # Get-Greeting sample function borrowed and modified from Bartek Bielawski's
            # blog at http://becomelotr.wordpress.com/2012/05/10/using-and-abusing-dynamic-parameters/
    
            function Get-Greeting {
                [CmdletBinding()]
                param (
                    $Name
                )
    
                DynamicParam {
                    # This check is here to make sure the mocked version can still work if the
                    # original function's dynamicparam block relied on script-scope variables.
                    if (-not $script:DoDynamicParam) { return }
    
                    if ($Name -cmatch '\b[a-z]') {
                        $Attributes = New-Object Management.Automation.ParameterAttribute
                        $Attributes.ParameterSetName = "__AllParameterSets"
                        $Attributes.Mandatory = $false
    
                        $AttributeCollection = New-Object Collections.ObjectModel.Collection[Attribute]
                        $AttributeCollection.Add($Attributes)
    
                        $Dynamic = New-Object System.Management.Automation.RuntimeDefinedParameter('Capitalize', [switch], $AttributeCollection)
    
                        $ParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                        $ParamDictionary.Add("Capitalize", $Dynamic)
                        $ParamDictionary
                    }
                }
    
                end
                {
                    if($PSBoundParameters.Capitalize) {
                        $Name = [regex]::Replace(
                            $Name,
                            '\b\w',
                            { $args[0].Value.ToUpper() }
                        )
                    }
    
                    "Welcome $Name!"
                }
            }
        } | Import-Module -Force
    
        $mockWith = { if (-not $Capitalize) { throw 'Capitalize variable not found, or set to false!' } }
        Mock Get-Greeting -MockWith $mockWith -ModuleName TestModule -ParameterFilter { [bool]$Capitalize }
    
        It 'Allows calls to be made with dynamic parameters (including parameter filters)' {
            { TestModule\PublicFunction } | Should Not Throw
            Assert-MockCalled Get-Greeting -ModuleName TestModule
        }
    
        Remove-Module TestModule -Force
    }
    
    Describe 'DynamicParam blocks in other scopes' {
        New-Module -Name TestModule1 {
            $script:DoDynamicParam = $true
    
            function DynamicParamFunction {
                [CmdletBinding()]
                param ( )
    
                DynamicParam {
                    if ($script:DoDynamicParam)
                    {
                        Get-MockDynamicParameters -CmdletName Get-ChildItem -Parameters @{ Path = [string[]]'Cert:\' }
                    }
                }
    
                end
                {
                    'I am the original function'
                }
            }
        } | Import-Module -Force
    
        New-Module -Name TestModule2 {
            function CallingFunction
            {
                DynamicParamFunction -CodeSigningCert
            }
    
            function CallingFunction2 {
                [CmdletBinding()]
                param (
                    [ValidateScript({ [bool](DynamicParamFunction -CodeSigningCert) })]
                    [string]
                    $Whatever
                )
            }
        } | Import-Module -Force
    
        Mock DynamicParamFunction { if ($CodeSigningCert) { 'I am the mocked function' } } -ModuleName TestModule2
    
        It 'Properly evaluates dynamic parameters when called from another scope' {
            CallingFunction | Should Be 'I am the mocked function'
        }
    
        It 'Properly evaluates dynamic parameters when called from another scope when the call is from a ValidateScript block' {
            CallingFunction2 -Whatever 'Whatever'
        }
    
        Remove-Module TestModule1 -Force
        Remove-Module TestModule2 -Force
    }
    
    Describe 'Parameter Filters and Common Parameters' {
        function Test-Function { [CmdletBinding()] param ( ) }
    
        Mock Test-Function { } -ParameterFilter { $VerbosePreference -eq 'Continue' }
    
        It 'Applies common parameters correctly when testing the parameter filter' {
            { Test-Function -Verbose } | Should Not Throw
            Assert-MockCalled Test-Function
            Assert-MockCalled Test-Function -ParameterFilter { $VerbosePreference -eq 'Continue' }
        }
    }
    
    Describe "Mocking Get-ItemProperty" {
        Mock Get-ItemProperty { New-Object -typename psobject -property @{ Name = "fakeName" } }
        It "Does not fail with NotImplementedException" {
            Get-ItemProperty -Path "HKLM:\Software\Key\" -Name "Property" | Select -ExpandProperty Name | Should Be fakeName
        }
    }
    
    Describe 'When mocking a command with parameters that match internal variable names' {
        function Test-Function
        {
            [CmdletBinding()]
            param (
                [string] $ArgumentList,
                [int] $FunctionName,
                [double] $ModuleName
            )
        }
    
        Mock Test-Function { return 'Mocked!' }
    
        It 'Should execute the mocked command successfully' {
            { Test-Function } | Should Not Throw
            Test-Function | Should Be 'Mocked!'
        }
    }
    
  • tools\Functions\Assertions\Should.ps1 Show
    function Parse-ShouldArgs([array] $shouldArgs) {
        if ($null -eq $shouldArgs) { $shouldArgs = @() }
    
        $parsedArgs = @{
            PositiveAssertion = $true
            ExpectedValue = $null
        }
    
        $assertionMethodIndex = 0
        $expectedValueIndex   = 1
    
        if ($shouldArgs.Count -gt 0 -and $shouldArgs[0].ToLower() -eq "not") {
            $parsedArgs.PositiveAssertion = $false
            $assertionMethodIndex += 1
            $expectedValueIndex   += 1
        }
    
        if ($assertionMethodIndex -lt $shouldArgs.Count)
        {
            $parsedArgs.AssertionMethod = "Pester$($shouldArgs[$assertionMethodIndex])"
        }
        else
        {
            throw 'You cannot call Should without specifying an assertion method.'
        }
    
        if ($expectedValueIndex -lt $shouldArgs.Count)
        {
            $parsedArgs.ExpectedValue = $shouldArgs[$expectedValueIndex]
        }
    
        return $parsedArgs
    }
    
    function Get-TestResult($shouldArgs, $value) {
        $assertionMethod = $shouldArgs.AssertionMethod
        $command = Get-Command $assertionMethod -ErrorAction (Get-IgnoreErrorPreference)
    
        if ($null -eq $command)
        {
            $assertionMethod = $assertionMethod -replace '^Pester'
            throw "'$assertionMethod' is not a valid Should operator."
        }
    
        $testResult = (& $assertionMethod $value $shouldArgs.ExpectedValue)
    
        if ($shouldArgs.PositiveAssertion) {
            return -not $testResult
        }
    
        return $testResult
    }
    
    function Get-FailureMessage($shouldArgs, $value) {
        $failureMessageFunction = "$($shouldArgs.AssertionMethod)FailureMessage"
        if (-not $shouldArgs.PositiveAssertion) {
            $failureMessageFunction = "Not$failureMessageFunction"
        }
    
        return (& $failureMessageFunction $value $shouldArgs.ExpectedValue)
    }
    function New-ShouldException ($Message,$Line) {
        $exception = New-Object Exception $Message
        $errorID = 'PesterAssertionFailed'
        $errorCategory = [Management.Automation.ErrorCategory]::InvalidResult
        $errorRecord = New-Object Management.Automation.ErrorRecord $exception, $errorID, $errorCategory, $null
        $errorRecord.ErrorDetails = "$Message failed at line: $line"
    
        $errorRecord
    }
    
    function Should {
        begin {
            Assert-DescribeInProgress -CommandName Should
            $parsedArgs = Parse-ShouldArgs $args
        }
    
        end {
            $input.MoveNext()
            do {
                $value = $input.Current
    
                $testFailed = Get-TestResult $parsedArgs $value
    
                if ($testFailed) {
                    $ShouldExceptionLine = $MyInvocation.ScriptLineNumber
                    $failureMessage = Get-FailureMessage $parsedArgs $value
    
    
                    throw ( New-ShouldException -Message $failureMessage -Line $ShouldExceptionLine )
                }
            } until ($input.MoveNext() -eq $false)
        }
    }
    
  • tools\Functions\Assertions\Should.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "Parse-ShouldArgs" {
            It "sanitizes assertions functions" {
                $parsedArgs = Parse-ShouldArgs TestFunction
                $parsedArgs.AssertionMethod | Should Be PesterTestFunction
            }
    
            It "works with strict mode when using 'switch' style tests" {
                Set-StrictMode -Version Latest
                { throw 'Test' } | Should Throw
            }
    
            Context "for positive assertions" {
    
                $parsedArgs = Parse-ShouldArgs testMethod, 1
    
                It "gets the expected value from the 2nd argument" {
                    $ParsedArgs.ExpectedValue | Should Be 1
                }
    
                It "marks the args as a positive assertion" {
                    $ParsedArgs.PositiveAssertion | Should Be $true
                }
            }
    
            Context "for negative assertions" {
    
                $parsedArgs = Parse-ShouldArgs Not, testMethod, 1
    
                It "gets the expected value from the third argument" {
                    $ParsedArgs.ExpectedValue | Should Be 1
                }
    
                It "marks the args as a negative assertion" {
                    $ParsedArgs.PositiveAssertion | Should Be $false
                }
            }
    
            Context "for the throw assertion" {
    
                $parsedArgs = Parse-ShouldArgs Throw
    
                It "translates the Throw assertion to PesterThrow" {
                    $ParsedArgs.AssertionMethod | Should Be PesterThrow
                }
    
            }
        }
    
        Describe "Get-TestResult" {
            Context "for positive assertions" {
                function PesterTest { return $true }
                $shouldArgs = Parse-ShouldArgs Test
    
                It "returns false if the test returns true" {
                    Get-TestResult $shouldArgs | Should Be $false
                }
            }
    
            Context "for negative assertions" {
                function PesterTest { return $false }
                $shouldArgs = Parse-ShouldArgs Not, Test
    
                It "returns false if the test returns false" {
                    Get-TestResult $shouldArgs | Should Be $false
                }
            }
        }
    
        Describe "Get-FailureMessage" {
            Context "for positive assertions" {
                function PesterTestFailureMessage($v, $e) { return "slime $e $v" }
                $shouldArgs = Parse-ShouldArgs Test, 1
    
                It "should return the postive assertion failure message" {
                    Get-FailureMessage $shouldArgs 2 | Should Be "slime 1 2"
                }
            }
    
            Context "for negative assertions" {
                function NotPesterTestFailureMessage($v, $e) { return "not slime $e $v" }
                $shouldArgs = Parse-ShouldArgs Not, Test, 1
    
                It "should return the negative assertion failure message" {
                  Get-FailureMessage $shouldArgs 2 | Should Be "not slime 1 2"
                }
            }
    
        }
    
        Describe -Tag "Acceptance" "Should" {
            It "can use the Be assertion" {
                1 | Should Be 1
            }
    
            It "can use the Not Be assertion" {
                1 | Should Not Be 2
            }
    
            It "can use the BeNullOrEmpty assertion" {
                $null | Should BeNullOrEmpty
                @()   | Should BeNullOrEmpty
                ""    | Should BeNullOrEmpty
            }
    
            It "can use the Not BeNullOrEmpty assertion" {
                @("foo") | Should Not BeNullOrEmpty
                "foo"    | Should Not BeNullOrEmpty
                "   "    | Should Not BeNullOrEmpty
                @(1,2,3) | Should Not BeNullOrEmpty
                12345    | Should Not BeNullOrEmpty
                $item1 = New-Object PSObject -Property @{Id=1; Name="foo"}
                $item2 = New-Object PSObject -Property @{Id=2; Name="bar"}
                @($item1, $item2) | Should Not BeNullOrEmpty
            }
    
            It "can handle exception thrown assertions" {
                { foo } | Should Throw
            }
    
            It "can handle exception should not be thrown assertions" {
                { $foo = 1 } | Should Not Throw
            }
    
            It "can handle Exist assertion" {
                $TestDrive | Should Exist
            }
    
            It "can handle the Match assertion" {
                "abcd1234" | Should Match "d1"
            }
    
            It "can test for file contents" {
                Setup -File "test.foo" "expected text"
                "$TestDrive\test.foo" | Should Contain "expected text"
            }
    
            It "ensures all assertion functions provide failure messages" {
                $assertionFunctions = @("PesterBe", "PesterThrow", "PesterBeNullOrEmpty", "PesterExist",
                    "PesterMatch", "PesterContain")
                $assertionFunctions | % {
                    "function:$($_)FailureMessage" | Should Exist
                    "function:Not$($_)FailureMessage" | Should Exist
                }
            }
    
            # TODO understand the purpose of this test, perhaps some better wording
            It "can process functions with empty output as input" {
                function ReturnNothing {}
    
                # TODO figure out why this is the case
                if ($PSVersionTable.PSVersion -eq "2.0") {
                    { $(ReturnNothing) | Should Not BeNullOrEmpty } | Should Not Throw
                } else {
                    { $(ReturnNothing) | Should Not BeNullOrEmpty } | Should Throw
                }
            }
    
        }
    }
    
  • tools\Functions\PesterState.ps1 Show
    function New-PesterState
    {
        param (
            [Parameter(Mandatory=$true)]
            [String]$Path,
            [String[]]$TagFilter,
            [String[]]$ExcludeTagFilter,
            [String[]]$TestNameFilter,
            [System.Management.Automation.SessionState]$SessionState,
            [Switch]$Strict,
            [Switch]$Quiet
        )
    
        if ($null -eq $SessionState) { $SessionState = $ExecutionContext.SessionState }
    
        New-Module -Name Pester -AsCustomObject -ScriptBlock {
            param (
                [String]$_path,
                [String[]]$_tagFilter,
                [String[]]$_excludeTagFilter,
                [String[]]$_testNameFilter,
                [System.Management.Automation.SessionState]$_sessionState,
                [Switch]$Strict,
                [Switch]$Quiet
            )
    
            #public read-only
            $Path = $_path
            $TagFilter = $_tagFilter
            $ExcludeTagFilter = $_excludeTagFilter
            $TestNameFilter = $_testNameFilter
    
            $script:SessionState = $_sessionState
            $script:CurrentContext = ""
            $script:CurrentDescribe = ""
            $script:CurrentTest = ""
            $script:Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
            $script:MostRecentTimestamp = 0
            $script:CommandCoverage = @()
            $script:BeforeEach = @()
            $script:AfterEach = @()
            $script:BeforeAll = @()
            $script:AfterAll = @()
            $script:Strict = $Strict
            $script:Quiet = $Quiet
    
            $script:TestResult = @()
    
            function EnterDescribe ($Name)
            {
                if ($CurrentDescribe)
                {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "You already are in Describe, you cannot enter Describe twice"
                }
                $script:CurrentDescribe = $Name
            }
    
            function LeaveDescribe
            {
                if ( $CurrentContext ) {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "Cannot leave Describe before leaving Context"
                }
    
                $script:CurrentDescribe = $null
            }
    
            function EnterContext ($Name)
            {
                if ( -not $CurrentDescribe )
                {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "Cannot enter Context before entering Describe"
                }
    
                if ( $CurrentContext )
                {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "You already are in Context, you cannot enter Context twice"
                }
    
                if ($CurrentTest)
                {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "You already are in It, you cannot enter Context inside It"
                }
    
                $script:CurrentContext = $Name
            }
    
            function LeaveContext
            {
                if ($CurrentTest)
                {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "Cannot leave Context before leaving It"
                }
    
                $script:CurrentContext = $null
            }
    
            function EnterTest([string]$Name)
            {
                if (-not $script:CurrentDescribe)
                {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "Cannot enter It before entering Describe"
                }
    
                if ( $CurrentTest )
                {
                    throw Microsoft.PowerShell.Utility\New-Object InvalidOperationException "You already are in It, you cannot enter It twice"
                }
    
                $script:CurrentTest = $Name
            }
    
            function LeaveTest
            {
                $script:CurrentTest = $null
            }
    
            function AddTestResult
            {
                param (
                    [string]$Name,
                    [ValidateSet("Failed","Passed","Skipped","Pending")]
                    [string]$Result,
                    [Nullable[TimeSpan]]$Time,
                    [string]$FailureMessage,
                    [string]$StackTrace,
                    [string] $ParameterizedSuiteName,
                    [System.Collections.IDictionary] $Parameters
                )
                $previousTime = $script:MostRecentTimestamp
                $script:MostRecentTimestamp = $script:Stopwatch.Elapsed
    
                if ($null -eq $Time)
                {
                    $Time = $script:MostRecentTimestamp - $previousTime
                }
    
                if (-not $script:Strict)
                {
                    $Passed = "Passed","Skipped","Pending" -contains $Result
                }
                else
                {
                    $Passed = $Result -eq "Passed"
                    if (($Result -eq "Skipped") -or ($Result -eq "Pending"))
                    {
                        $FailureMessage = "The test failed because the test was executed in Strict mode and the result '$result' was translated to Failed."
                        $Result = "Failed"
                    }
    
                }
    
                $Script:TestResult += Microsoft.PowerShell.Utility\New-Object -TypeName PsObject -Property @{
                    Describe               = $CurrentDescribe
                    Context                = $CurrentContext
                    Name                   = $Name
                    Passed                 = $Passed
                    Result                 = $Result
                    Time                   = $Time
                    FailureMessage         = $FailureMessage
                    StackTrace             = $StackTrace
                    ParameterizedSuiteName = $ParameterizedSuiteName
                    Parameters             = $Parameters
                } | Microsoft.PowerShell.Utility\Select-Object Describe, Context, Name, Result, Passed, Time, FailureMessage, StackTrace, ParameterizedSuiteName, Parameters
            }
    
            $ExportedVariables = "Path",
            "TagFilter",
            "ExcludeTagFilter",
            "TestNameFilter",
            "TestResult",
            "CurrentContext",
            "CurrentDescribe",
            "CurrentTest",
            "SessionState",
            "CommandCoverage",
            "BeforeEach",
            "AfterEach",
            "BeforeAll",
            "AfterAll",
            "Strict",
            "Quiet"
    
            $ExportedFunctions = "EnterContext",
            "LeaveContext",
            "EnterDescribe",
            "LeaveDescribe",
            "EnterTest",
            "LeaveTest",
            "AddTestResult"
    
            Export-ModuleMember -Variable $ExportedVariables -function $ExportedFunctions
        } -ArgumentList $Path, $TagFilter, $ExcludeTagFilter, $TestNameFilter, $SessionState, $Strict, $Quiet |
        Add-Member -MemberType ScriptProperty -Name TotalCount -Value {
            @( $this.TestResult ).Count
        } -PassThru |
        Add-Member -MemberType ScriptProperty -Name PassedCount -Value {
            @( $this.TestResult | where { $_.Result -eq "Passed" } ).count
        } -PassThru |
        Add-Member -MemberType ScriptProperty -Name FailedCount -Value {
            @( $this.TestResult | where { $_.Result -eq "Failed" } ).count
        } -PassThru |
        Add-Member -MemberType ScriptProperty -Name SkippedCount -Value {
            @( $this.TestResult | where { $_.Result -eq "Skipped" } ).count
        } -PassThru |
        Add-Member -MemberType ScriptProperty -Name PendingCount -Value {
            @( $this.TestResult | where { $_.Result -eq "Pending" } ).count
        } -PassThru |
        Add-Member -MemberType ScriptProperty -Name Time -Value {
            $this.TestResult | foreach { [timespan]$total=0 } { $total = $total + ( $_.time ) } { [timespan]$total }
        } -PassThru |
        Add-Member -MemberType ScriptProperty -Name Scope -Value {
            if ($this.CurrentTest) { 'It' }
            elseif ($this.CurrentContext)  { 'Context' }
            elseif ($this.CurrentDescribe) { 'Describe' }
            else { $null }
        } -Passthru |
        Add-Member -MemberType ScriptProperty -Name ParentScope -Value {
            $parentScope = $null
            $scope = $this.Scope
    
            if ($scope -eq 'It' -and $this.CurrentContext)
            {
                $parentScope = 'Context'
            }
    
            if ($null -eq $parentScope -and $scope -ne 'Describe' -and $this.CurrentDescribe)
            {
                $parentScope = 'Describe'
            }
    
            return $parentScope
        } -PassThru
    }
    
    function Write-Describe
    {
        param (
            [Parameter(mandatory=$true, valueFromPipeline=$true)]$Name
        )
        process {
            Write-Screen Describing $Name -OutputType Header 
        }
    }
    
    function Write-Context
    {
        param (
            [Parameter(mandatory=$true, valueFromPipeline=$true)]$Name
        )
        process {
            $margin = " " * 3
            Write-Screen ${margin}Context $Name -OutputType Header
        }
    }
    
    function Write-PesterResult
    {
        param (
            [Parameter(mandatory=$true, valueFromPipeline=$true)]
            $TestResult
        )
        process {
            $testDepth = if ( $TestResult.Context ) { 4 } elseif ( $TestResult.Describe ) { 1 } else { 0 }
    
            $margin = " " * $TestDepth
            $error_margin = $margin + "  "
            $output = $TestResult.name
            $humanTime = Get-HumanTime $TestResult.Time.TotalSeconds
    
            switch ($TestResult.Result)
            {
                Passed {
                    "$margin[+] $output $humanTime" | Write-Screen -OutputType Passed
                    break
                }
                Failed {
                    "$margin[-] $output $humanTime" | Write-Screen -OutputType Failed
                    Write-Screen -OutputType Failed $($TestResult.failureMessage -replace '(?m)^',$error_margin)
                    Write-Screen -OutputType Failed $($TestResult.stackTrace -replace '(?m)^',$error_margin)
                    break
                }
                Skipped {
                    "$margin[!] $output $humanTime" | Write-Screen -OutputType Skipped
                    break
                }
                Pending {
                    "$margin[?] $output $humanTime" | Write-Screen -OutputType Pending
                    break
                }
            }
        }
    }
    
    function Write-PesterReport
    {
        param (
            [Parameter(mandatory=$true, valueFromPipeline=$true)]
            $PesterState
        )
    
        Write-Screen "Tests completed in $(Get-HumanTime $PesterState.Time.TotalSeconds)"
        Write-Screen "Passed: $($PesterState.PassedCount) Failed: $($PesterState.FailedCount) Skipped: $($PesterState.SkippedCount) Pending: $($PesterState.PendingCount)"
    }
    
    function Write-Screen {
        #wraps the Write-Host cmdlet to control if the output is written to screen from one place
        param(
            #Write-Host parameters
            [Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)]
            [Object] $Object,
            [Switch] $NoNewline,
            [Object] $Separator,
            #custom parameters
            [Switch] $Quiet = $pester.Quiet,
            [ValidateSet("Failed","Passed","Skipped","Pending","Header","Standard")]
            [String] $OutputType = "Standard"
        )
    
        begin
        {
            if ($Quiet) { return }
            
            #make the bound parameters compatible with Write-Host
            if ($PSBoundParameters.ContainsKey('Quiet')) { $PSBoundParameters.Remove('Quiet') | Out-Null }
            if ($PSBoundParameters.ContainsKey('OutputType')) { $PSBoundParameters.Remove('OutputType') | Out-Null}
            
            if ($OutputType -ne "Standard")
            {
                #create the key first to make it work in strict mode
                if (-not $PSBoundParameters.ContainsKey('ForegroundColor'))
                { 
                    $PSBoundParameters.Add('ForegroundColor', $null)
                }
    
                
                
                switch ($Host.Name) 
                {
                    #light background
                    "PowerGUIScriptEditorHost" {
                        $ColorSet = @{ 
                            Failed  = [ConsoleColor]::Red
                            Passed  = [ConsoleColor]::DarkGreen
                            Skipped = [ConsoleColor]::DarkGray
                            Pending = [ConsoleColor]::DarkCyan
                            Header  = [ConsoleColor]::Magenta
                        }
                    }
                    #dark background
                    { "Windows PowerShell ISE Host", "ConsoleHost" -contains $_ } {
                        $ColorSet = @{ 
                            Failed  = [ConsoleColor]::Red
                            Passed  = [ConsoleColor]::Green
                            Skipped = [ConsoleColor]::Gray
                            Pending = [ConsoleColor]::Cyan
                            Header  = [ConsoleColor]::Magenta
                        }
                    }
                    default {
                        $ColorSet = @{ 
                            Failed  = [ConsoleColor]::Red
                            Passed  = [ConsoleColor]::DarkGreen
                            Skipped = [ConsoleColor]::Gray
                            Pending = [ConsoleColor]::Gray
                            Header  = [ConsoleColor]::Magenta
                        }
                    }
                    
                 }
    
                
                $PSBoundParameters.ForegroundColor = $ColorSet.$OutputType
            }
            
            try {
                $outBuffer = $null
                if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
                {
                    $PSBoundParameters['OutBuffer'] = 1
                }
                $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Write-Host', [System.Management.Automation.CommandTypes]::Cmdlet)
                $scriptCmd = {& $wrappedCmd @PSBoundParameters }
                $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
                $steppablePipeline.Begin($PSCmdlet)
            } catch {
                throw
            }
        }
    
        process
        {
            if ($Quiet) { return }
            try {
                $steppablePipeline.Process($_)
            } catch {
                throw
            }
        }
    
        end
        {
            if ($Quiet) { return }
            try {
                $steppablePipeline.End()
            } catch {
                throw
            }
        }
    }
  • tools\Functions\PesterState.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "New-PesterState" {
            it "Path is mandatory parameter" {
                (get-command New-PesterState ).Parameters.Path.ParameterSets.__AllParameterSets.IsMandatory | Should Be $true
            }
    
            Context "Path parameter is set" {
                it "sets the path property" {
                    $p = new-pesterstate -path "path"
                    $p.Path | should be  "path"
                }
            }
    
            Context "Path and TestNameFilter parameter is set" {
                $p = new-pesterstate -path "path" -TestNameFilter "filter"
    
                it "sets the path property" {
                    $p.Path | should be  "path"
                }
    
                it "sets the TestNameFilter property" {
                    $p.TestNameFilter | should be "filter"
                }
    
            }
            Context "Path and TagFilter parameter are set" {
                $p = new-pesterstate -path "path" -TagFilter "tag","tag2"
    
                it "sets the path property" {
                    $p.Path | should be  "path"
                }
    
                it "sets the TestNameFilter property" {
                    $p.TagFilter | should be ("tag","tag2")
                }
            }
            Context "Path and ExcludeTagFilter parameter are set" {
                $p = new-pesterstate -path "path" -ExcludeTagFilter "tag3", "tag"
    
                it "sets the path property" {
                    $p.Path | should be  "path"
                }
    
                it "sets the ExcludeTagFilter property" {
                    $p.ExcludeTagFilter | should be ("tag3", "tag")
                }
            }        
            Context "Path, TagFilter and ExcludeTagFilter parameter are set" {
                $p = new-pesterstate -path "path" -TagFilter "tag","tag2" -ExcludeTagFilter "tag3"
    
                it "sets the path property" {
                    $p.Path | should be  "path"
                }
    
                it "sets the TestNameFilter property" {
                    $p.TagFilter | should be ("tag","tag2")
                }
    
                it "sets the ExcludeTagFilter property" {
                    $p.ExcludeTagFilter | should be ("tag3")
                }
            }
            Context "Path TestNameFilter and TagFilter parameter is set" {
                $p = new-pesterstate -path "path" -TagFilter "tag","tag2" -testnamefilter "filter"
    
                it "sets the path property" {
                    $p.Path | should be  "path"
                }
    
                it "sets the TestNameFilter property" {
                    $p.TagFilter | should be ("tag","tag2")
                }
    
                it "sets the TestNameFilter property" {
                    $p.TagFilter | should be ("tag","tag2")
                }
    
            }
        }
    
        Describe "Pester state object" {
            $p = New-PesterState -Path "Local"
    
            Context "entering describe" {
                It "enters describe" {
                    $p.EnterDescribe("describe")
                    $p.CurrentDescribe | should be "Describe"
                }
                It "can enter describe only once" {
                    { $p.EnterDescribe("describe") } | Should Throw
                }
    
                It "Reports scope correctly" {
                    $p.Scope | should be "describe"
                }
            }
            Context "leaving describe" {
                It "leaves describe" {
                    $p.LeaveDescribe()
                    $p.CurrentDescribe | should benullOrEmpty
                }
                It "Reports scope correctly" {
                    $p.Scope | should benullOrEmpty
                }
            }
    
            context "Entering It from Describe" {
                $p.EnterDescribe('Describe')
    
                It "Enters It successfully" {
                    { $p.EnterTest("It") } | Should Not Throw
                }
    
                It "Reports scope correctly" {
                    $p.Scope | Should Be 'It'
                }
    
                It "Cannot enter It after already entered" {
                    { $p.EnterTest("It") } | Should Throw
                }
    
                It "Cannot enter Context from inside It" {
                    { $p.EnterContext("Context") } | Should Throw
                }
            }
    
            context "Leaving It from Describe" {
                It "Leaves It to Describe" {
                    { $p.LeaveTest() } | Should Not Throw
                }
    
                It "Reports scope correctly" {
                    $p.Scope | Should Be 'Describe'
                }
    
                $p.LeaveDescribe()
            }
    
            Context "entering Context" {
                it "Cannot enter Context before Describe" {
                    { $p.EnterContext("context") } | should throw
                }
    
                it "enters context from describe" {
                    $p.EnterDescribe("Describe")
                    $p.EnterContext("Context")
                    $p.CurrentContext | should be "Context"
                }
                It "can enter context only once" {
                    { $p.EnterContext("Context") } | Should Throw
                }
    
                It "Reports scope correctly" {
                    $p.Scope | should be "Context"
                }
            }
    
            Context "leaving context" {
                it "cannot leave describe before leaving context" {
                    { $p.LeaveDescribe() } | should throw
                }
                it "leaves context" {
                    $p.LeaveContext()
                    $p.CurrentContext | should BeNullOrEmpty
                }
                It "Returns from context to describe" {
                    $p.Scope | should be "Describe"
                }
    
                $p.LeaveDescribe()
            }
    
            context "Entering It from Context" {
                $p.EnterDescribe('Describe')
                $p.EnterContext('Context')
    
                It "Enters It successfully" {
                    { $p.EnterTest("It") } | Should Not Throw
                }
    
                It "Reports scope correctly" {
                    $p.Scope | Should Be 'It'
                }
    
                It "Cannot enter It after already entered" {
                    { $p.EnterTest("It") } | Should Throw
                }
            }
    
            context "Leaving It from Context" {
                It "Leaves It to Context" {
                    { $p.LeaveTest() } | Should Not Throw
                }
    
                It "Reports scope correctly" {
                    $p.Scope | Should Be 'Context'
                }
    
                $p.LeaveContext()
                $p.LeaveDescribe()
            }
    
            context "adding test result" {
                $p.EnterDescribe('Describe')
    
                it "adds passed test" {
                    $p.AddTestResult("result","Passed", 100)
                    $result = $p.TestResult[-1]
                    $result.Name | should be "result"
                    $result.passed | should be $true
                    $result.Result | Should be "Passed"
                    $result.time.ticks | should be 100
                }
                it "adds failed test" {
                    $p.AddTestResult("result","Failed", 100, "fail", "stack")
                    $result = $p.TestResult[-1]
                    $result.Name | should be "result"
                    $result.passed | should be $false
                    $result.Result | Should be "Failed"
                    $result.time.ticks | should be 100
                    $result.FailureMessage | should be "fail"
                    $result.StackTrace | should be "stack"
                }
    
                it "adds skipped test" {
                    $p.AddTestResult("result","Skipped", 100)
                    $result = $p.TestResult[-1]
                    $result.Name | should be "result"
                    $result.passed | should be $true
                    $result.Result | Should be "Skipped"
                    $result.time.ticks | should be 100
                }
    
                it "adds Pending test" {
                    $p.AddTestResult("result","Pending", 100)
                    $result = $p.TestResult[-1]
                    $result.Name | should be "result"
                    $result.passed | should be $true
                    $result.Result | Should be "Pending"
                    $result.time.ticks | should be 100
                }
    
                it "can add test result before entering describe" {
                    if ($p.CurrentContext) { $p.LeaveContext()}
                    if ($p.CurrentDescribe) { $p.LeaveDescribe() }
                    { $p.addTestResult(1,"Passed",1) } | should not throw
                }
    
                $p.LeaveContext()
                $p.LeaveDescribe()
    
            }
    
            Context "Path and TestNameFilter parameter is set" {
                $strict = New-PesterState -path "path" -Strict
    
                It "Keeps Passed state" {
                    $strict.AddTestResult("test","Passed")
                    $result = $strict.TestResult[-1]
    
                    $result.passed | should be $true
                    $result.Result | Should be "Passed"
                }
    
                It "Keeps Failed state" {
                    $strict.AddTestResult("test","Failed")
                    $result = $strict.TestResult[-1]
    
                    $result.passed | should be $false
                    $result.Result | Should be "Failed"
                }
    
                It "Changes Pending state to Failed" {
                    $strict.AddTestResult("test","Pending")
                    $result = $strict.TestResult[-1]
    
                    $result.passed | should be $false
                    $result.Result | Should be "Failed"
                }
    
                It "Changes Skipped state to Failed" {
                    $strict.AddTestResult("test","Skipped")
                    $result = $strict.TestResult[-1]
    
                    $result.passed | should be $false
                    $result.Result | Should be "Failed"
                }
            }
        }
    }
    
  • tools\Functions\SetupTeardown.ps1 Show
    function BeforeEach
    {
    <#
    .SYNOPSIS
        Defines a series of steps to perform at the beginning of every It block within
        the current Context or Describe block.
    
    .DESCRIPTION
        BeforeEach, AfterEach, BeforeAll, and AfterAll are unique in that they apply
        to the entire Context or Describe block, regardless of the order of the
        statements in the Context or Describe.  For a full description of this
        behavior, as well as how multiple BeforeEach or AfterEach blocks interact
        with each other, please refer to the about_BeforeEach_AfterEach help file.
    
    .LINK
        about_BeforeEach_AfterEach
    #>
        Assert-DescribeInProgress -CommandName BeforeEach
    }
    
    function AfterEach
    {
    <#
    .SYNOPSIS
        Defines a series of steps to perform at the end of every It block within
        the current Context or Describe block.
    
    .DESCRIPTION
        BeforeEach, AfterEach, BeforeAll, and AfterAll are unique in that they apply
        to the entire Context or Describe block, regardless of the order of the
        statements in the Context or Describe.  For a full description of this
        behavior, as well as how multiple BeforeEach or AfterEach blocks interact
        with each other, please refer to the about_BeforeEach_AfterEach help file.
    
    .LINK
        about_BeforeEach_AfterEach
    #>
        Assert-DescribeInProgress -CommandName AfterEach
    }
    
    function BeforeAll
    {
    <#
    .SYNOPSIS
        Defines a series of steps to perform at the beginning of the current Context
        or Describe block.
    
    .DESCRIPTION
        BeforeEach, AfterEach, BeforeAll, and AfterAll are unique in that they apply
        to the entire Context or Describe block, regardless of the order of the
        statements in the Context or Describe.
    
    .LINK
        about_BeforeEach_AfterEach
    #>
        Assert-DescribeInProgress -CommandName BeforeAll
    }
    
    function AfterAll
    {
    <#
    .SYNOPSIS
        Defines a series of steps to perform at the end of every It block within
        the current Context or Describe block.
    
    .DESCRIPTION
        BeforeEach, AfterEach, BeforeAll, and AfterAll are unique in that they apply
        to the entire Context or Describe block, regardless of the order of the
        statements in the Context or Describe.
    
    .LINK
        about_BeforeEach_AfterEach
    #>
        Assert-DescribeInProgress -CommandName AfterAll
    }
    
    function Clear-SetupAndTeardown
    {
        $pester.BeforeEach = @( $pester.BeforeEach | Where-Object { $_.Scope -ne $pester.Scope } )
        $pester.AfterEach  = @( $pester.AfterEach  | Where-Object { $_.Scope -ne $pester.Scope } )
        $pester.BeforeAll  = @( $pester.BeforeAll  | Where-Object { $_.Scope -ne $pester.Scope } )
        $pester.AfterAll   = @( $pester.AfterAll   | Where-Object { $_.Scope -ne $pester.Scope } )
    }
    
    function Invoke-TestCaseSetupBlocks
    {
        $orderedSetupBlocks = @(
            $pester.BeforeEach | Where-Object { $_.Scope -eq 'Describe' } | Select-Object -ExpandProperty ScriptBlock
            $pester.BeforeEach | Where-Object { $_.Scope -eq 'Context'  } | Select-Object -ExpandProperty ScriptBlock
        )
    
        Invoke-Blocks -ScriptBlock $orderedSetupBlocks
    }
    
    function Invoke-TestCaseTeardownBlocks
    {
        $orderedTeardownBlocks = @(
            $pester.AfterEach | Where-Object { $_.Scope -eq 'Context'  } | Select-Object -ExpandProperty ScriptBlock
            $pester.AfterEach | Where-Object { $_.Scope -eq 'Describe' } | Select-Object -ExpandProperty ScriptBlock
        )
    
        Invoke-Blocks -ScriptBlock $orderedTeardownBlocks
    }
    
    function Invoke-TestGroupSetupBlocks
    {
        param ([string] $Scope)
    
        $scriptBlocks = $pester.BeforeAll |
                        Where-Object { $_.Scope -eq $Scope } |
                        Select-Object -ExpandProperty ScriptBlock
    
        Invoke-Blocks -ScriptBlock $scriptBlocks
    }
    
    function Invoke-TestGroupTeardownBlocks
    {
        param ([string] $Scope)
    
        $scriptBlocks = $pester.AfterAll |
                        Where-Object { $_.Scope -eq $Scope } |
                        Select-Object -ExpandProperty ScriptBlock
    
        Invoke-Blocks -ScriptBlock $scriptBlocks
    }
    
    function Invoke-Blocks
    {
        param ([scriptblock[]] $ScriptBlock)
    
        foreach ($block in $ScriptBlock)
        {
            if ($null -eq $block) { continue }
    
            try
            {
                . $block
            }
            catch
            {
                Write-Error -ErrorRecord $_
            }
        }
    }
    
    function Add-SetupAndTeardown
    {
        param (
            [scriptblock] $ScriptBlock
        )
    
        $codeText = $ScriptBlock.ToString()
        $tokens = ParseCodeIntoTokens -CodeText $codeText
    
        for ($i = 0; $i -lt $tokens.Count; $i++)
        {
            if ($tokens[$i].Type -eq [System.Management.Automation.PSTokenType]::Command -and
                (IsSetupOrTeardownCommand -CommandName $tokens[$i].Content))
            {
                $openBraceIndex, $closeBraceIndex = Get-BraceIndecesForCommand -Tokens $tokens -CommandIndex $i
                Add-SetupTeardownFromTokens -Tokens $tokens -CommandIndex $i -OpenBraceIndex $openBraceIndex -CloseBraceIndex $closeBraceIndex -CodeText $codeText
                $i = $closeBraceIndex
            }
            elseif ($tokens[$i].Type -eq [System.Management.Automation.PSTokenType]::GroupStart)
            {
                # We don't want to parse Setup or Teardown commands in child scopes here, so anything
                # bounded by a GroupStart / GroupEnd token pair which is not immediately preceded by
                # a setup / teardown command name is ignored.
                $i = Get-GroupCloseTokenIndex -Tokens $tokens -GroupStartTokenIndex $i
            }
        }
    }
    
    function ParseCodeIntoTokens
    {
        param ([string] $CodeText)
    
        $parseErrors = $null
        $tokens = [System.Management.Automation.PSParser]::Tokenize($CodeText, [ref] $parseErrors)
    
        if ($parseErrors.Count -gt 0)
        {
            $currentScope = $pester.Scope
            throw "The current $currentScope block contains syntax errors."
        }
    
        return $tokens
    }
    
    function IsSetupOrTeardownCommand
    {
        param ([string] $CommandName)
        return (IsSetupCommand -CommandName $CommandName) -or (IsTeardownCommand -CommandName $CommandName)
    }
    
    function IsSetupCommand
    {
        param ([string] $CommandName)
        return $CommandName -eq 'BeforeEach' -or $CommandName -eq 'BeforeAll'
    }
    
    function IsTeardownCommand
    {
        param ([string] $CommandName)
        return $CommandName -eq 'AfterEach' -or $CommandName -eq 'AfterAll'
    }
    
    function IsTestGroupCommand
    {
        param ([string] $CommandName)
        return $CommandName -eq 'BeforeAll' -or $CommandName -eq 'AfterAll'
    }
    
    function Get-BraceIndecesForCommand
    {
        param (
            [System.Management.Automation.PSToken[]] $Tokens,
            [int] $CommandIndex
        )
    
        $openingGroupTokenIndex = Get-GroupStartTokenForCommand -Tokens $Tokens -CommandIndex $CommandIndex
        $closingGroupTokenIndex = Get-GroupCloseTokenIndex -Tokens $Tokens -GroupStartTokenIndex $openingGroupTokenIndex
    
        return $openingGroupTokenIndex, $closingGroupTokenIndex
    }
    
    function Get-GroupStartTokenForCommand
    {
        param (
            [System.Management.Automation.PSToken[]] $Tokens,
            [int] $CommandIndex
        )
    
        # We may want to allow newlines, other parameters, etc at some point.  For now it's good enough to
        # just verify that the next token after our BeforeEach or AfterEach command is an opening curly brace.
    
        $commandName = $Tokens[$CommandIndex].Content
    
        if ($CommandIndex + 1 -ge $tokens.Count -or
            $tokens[$CommandIndex + 1].Type -ne [System.Management.Automation.PSTokenType]::GroupStart -or
            $tokens[$CommandIndex + 1].Content -ne '{')
        {
            throw "The $commandName command must be immediately followed by the opening brace of a script block."
        }
    
        return $CommandIndex + 1
    }
    
    function Get-GroupCloseTokenIndex
    {
        param (
            [System.Management.Automation.PSToken[]] $Tokens,
            [int] $GroupStartTokenIndex
        )
    
        $groupLevel = 1
    
        for ($i = $GroupStartTokenIndex + 1; $i -lt $Tokens.Count; $i++)
        {
            switch ($Tokens[$i].Type)
            {
                ([System.Management.Automation.PSTokenType]::GroupStart)
                {
                    $groupLevel++
                    break
                }
    
                ([System.Management.Automation.PSTokenType]::GroupEnd)
                {
                    $groupLevel--
    
                    if ($groupLevel -le 0)
                    {
                        return $i
                    }
    
                    break
                }
            }
        }
    
        throw 'No corresponding GroupEnd token was found.'
    }
    
    function Add-SetupTeardownFromTokens
    {
        param (
            [System.Management.Automation.PSToken[]] $Tokens,
            [int] $CommandIndex,
            [int] $OpenBraceIndex,
            [int] $CloseBraceIndex,
            [string] $CodeText
        )
    
        $commandName = $Tokens[$CommandIndex].Content
    
        $blockStart = $Tokens[$OpenBraceIndex + 1].Start
        $blockLength = $Tokens[$CloseBraceIndex].Start - $blockStart
        $setupOrTeardownCodeText = $codeText.Substring($blockStart, $blockLength)
    
        $setupOrTeardownBlock = [scriptblock]::Create($setupOrTeardownCodeText)
        Set-ScriptBlockScope -ScriptBlock $setupOrTeardownBlock -SessionState $pester.SessionState
    
        $isSetupCommand = IsSetupCommand -CommandName $commandName
        $isGroupCommand = IsTestGroupCommand -CommandName $commandName
    
        if ($isSetupCommand)
        {
            if ($isGroupCommand)
            {
                Add-BeforeAll -ScriptBlock $setupOrTeardownBlock
            }
            else
            {
                Add-BeforeEach -ScriptBlock $setupOrTeardownBlock
            }
        }
        else
        {
            if ($isGroupCommand)
            {
                Add-AfterAll -ScriptBlock $setupOrTeardownBlock
            }
            else
            {
                Add-AfterEach -ScriptBlock $setupOrTeardownBlock
            }
        }
    }
    
    function Add-BeforeEach
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock
        )
    
        $props = @{
            Scope       = $pester.Scope
            ScriptBlock = $ScriptBlock
        }
    
        $pester.BeforeEach += @(New-Object psobject -Property $props)
    }
    
    function Add-AfterEach
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock
        )
    
        $props = @{
            Scope       = $pester.Scope
            ScriptBlock = $ScriptBlock
        }
    
        $pester.AfterEach += @(New-Object psobject -Property $props)
    }
    
    function Add-BeforeAll
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock
        )
    
        $props = @{
            Scope       = $pester.Scope
            ScriptBlock = $ScriptBlock
        }
    
        $pester.BeforeAll += @(New-Object psobject -Property $props)
    }
    
    function Add-AfterAll
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock
        )
    
        $props = @{
            Scope       = $pester.Scope
            ScriptBlock = $ScriptBlock
        }
    
        $pester.AfterAll += @(New-Object psobject -Property $props)
    }
    
  • tools\Functions\SetupTeardown.Tests.ps1 Show
    Describe 'Describe-Scoped Test Case setup' {
        BeforeEach {
            $testVariable = 'From BeforeEach'
        }
    
        $testVariable = 'Set in Describe'
    
        It 'Assigns the correct value in first test' {
            $testVariable | Should Be 'From BeforeEach'
            $testVariable = 'Set in It'
        }
    
        It 'Assigns the correct value in subsequent tests' {
            $testVariable | Should Be 'From BeforeEach'
        }
    }
    
    Describe 'Context-scoped Test Case setup' {
        $testVariable = 'Set in Describe'
    
        Context 'The context' {
            BeforeEach {
                $testVariable = 'From BeforeEach'
            }
    
            It 'Assigns the correct value inside the context' {
                $testVariable | Should Be 'From BeforeEach'
            }
        }
    
        It 'Reports the original value after the Context' {
            $testVariable | Should Be 'Set in Describe'
        }
    }
    
    Describe 'Multiple Test Case setup blocks' {
        $testVariable = 'Set in Describe'
    
        BeforeEach {
            $testVariable = 'Set in Describe BeforeEach'
        }
    
        Context 'The context' {
            It 'Executes Describe setup blocks first, then Context blocks in the order they were defined (even if they are defined after the It block.)' {
                $testVariable | Should Be 'Set in the second Context BeforeEach'
            }
    
            BeforeEach {
                $testVariable = 'Set in the first Context BeforeEach'
            }
    
            BeforeEach {
                $testVariable = 'Set in the second Context BeforeEach'
            }
        }
    
        It 'Continues to execute Describe setup blocks after the Context' {
            $testVariable | Should Be 'Set in Describe BeforeEach'
        }
    }
    
    Describe 'Describe-scoped Test Case teardown' {
        $testVariable = 'Set in Describe'
    
        AfterEach {
            $testVariable = 'Set in AfterEach'
        }
    
        It 'Does not modify the variable before the first test' {
            $testVariable | Should Be 'Set in Describe'
        }
    
        It 'Modifies the variable after the first test' {
            $testVariable | Should Be 'Set in AfterEach'
        }
    }
    
    Describe 'Multiple Test Case teardown blocks' {
        $testVariable = 'Set in Describe'
    
        AfterEach {
            $testVariable = 'Set in Describe AfterEach'
        }
    
        Context 'The context' {
            AfterEach {
                $testVariable = 'Set in the first Context AfterEach'
            }
    
            It 'Performs a test in Context' { "some output to prevent the It being marked as Pending and failing because of Strict mode"}
    
            It 'Executes Describe teardown blocks after Context teardown blocks' {
                $testVariable | Should Be 'Set in the second Describe AfterEach'
            }
        }
    
        AfterEach {
            $testVariable = 'Set in the second Describe AfterEach'
        }
    }
    
    $script:DescribeBeforeAllCounter = 0
    $script:DescribeAfterAllCounter  = 0
    $script:ContextBeforeAllCounter  = 0
    $script:ContextAfterAllCounter   = 0
    
    Describe 'Test Group Setup and Teardown' {
        It 'Executed the Describe BeforeAll regardless of definition order' {
            $script:DescribeBeforeAllCounter | Should Be 1
        }
    
        It 'Did not execute any other block yet' {
            $script:DescribeAfterAllCounter | Should Be 0
            $script:ContextBeforeAllCounter | Should Be 0
            $script:ContextAfterAllCounter  | Should Be 0
        }
    
        BeforeAll {
            $script:DescribeBeforeAllCounter++
        }
    
        AfterAll {
            $script:DescribeAfterAllCounter++
        }
    
        Context 'Context scoped setup and teardown' {
            BeforeAll {
                $script:ContextBeforeAllCounter++
            }
    
            AfterAll {
                $script:ContextAfterAllCounter++
            }
    
            It 'Executed the Context BeforeAll block' {
                $script:ContextBeforeAllCounter | Should Be 1
            }
    
            It 'Has not executed any other blocks yet' {
                $script:DescribeBeforeAllCounter | Should Be 1
                $script:DescribeAfterAllCounter  | Should Be 0
                $script:ContextAfterAllCounter   | Should Be 0
            }
        }
    
        It 'Executed the Context AfterAll block' {
            $script:ContextAfterAllCounter | Should Be 1
        }
    }
    
    Describe 'Finishing TestGroup Setup and Teardown tests' {
        It 'Executed each Describe and Context group block once' {
            $script:DescribeBeforeAllCounter | Should Be 1
            $script:DescribeAfterAllCounter  | Should Be 1
            $script:ContextBeforeAllCounter  | Should Be 1
            $script:ContextAfterAllCounter   | Should Be 1
        }
    }
  • tools\Functions\Assertions\Test-Assertion.ps1 Show
    function Test-PositiveAssertion($result) {
        if (-not $result) {
            throw "Expecting expression to pass, but it failed"
        }
    }
    
    function Test-NegativeAssertion($result) {
        if ($result) {
            throw "Expecting expression to pass, but it failed"
        }
    }
    
    
  • tools\Functions\Context.ps1 Show
    function Context {
    <#
    .SYNOPSIS
    Provides logical grouping of It blocks within a single Describe block. Any Mocks defined
    inside a Context are removed at the end of the Context scope, as are any files or folders
    added to the TestDrive during the Context block's execution. Any BeforeEach or AfterEach
    blocks defined inside a Context also only apply to tests within that Context .
    
    .PARAMETER Name
    The name of the Context. This is a phrase describing a set of tests within a describe.
    
    .PARAMETER Fixture
    Script that is executed. This may include setup specific to the context and one or more It
    blocks that validate the expected outcomes.
    
    .EXAMPLE
    function Add-Numbers($a, $b) {
        return $a + $b
    }
    
    Describe "Add-Numbers" {
    
        Context "when root does not exist" {
             It "..." { ... }
        }
    
        Context "when root does exist" {
            It "..." { ... }
            It "..." { ... }
            It "..." { ... }
        }
    }
    
    .LINK
    Describe
    It
    BeforeEach
    AfterEach
    about_Should
    about_Mocking
    about_TestDrive
    
    #>
        param(
            [Parameter(Mandatory = $true)]
            [string] $Name,
    
            [ValidateNotNull()]
            [ScriptBlock] $Fixture  = $(Throw "No test script block is provided. (Have you put the open curly brace on the next line?)")
        )
    
        Assert-DescribeInProgress -CommandName Context
    
        $Pester.EnterContext($Name )
        $TestDriveContent = Get-TestDriveChildItem
    
        $Pester.CurrentContext | Write-Context
    
        try
        {
            Add-SetupAndTeardown -ScriptBlock $Fixture
            Invoke-TestGroupSetupBlocks -Scope $pester.Scope
            $null = & $Fixture
        }
        catch
        {
            $firstStackTraceLine = $_.InvocationInfo.PositionMessage.Trim() -split '\r?\n' | Select-Object -First 1
            $Pester.AddTestResult('Error occurred in Context block', "Failed", $null, $_.Exception.Message, $firstStackTraceLine)
            $Pester.TestResult[-1] | Write-PesterResult
        }
        finally
        {
            Invoke-TestGroupTeardownBlocks -Scope $pester.Scope
        }
    
        Clear-SetupAndTeardown
        Clear-TestDrive -Exclude ($TestDriveContent | select -ExpandProperty FullName)
        Exit-MockScope
        $Pester.LeaveContext()
    }
    
    
  • tools\Functions\Assertions\Contain.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterContain" {
            Context "when testing file contents" {
                Setup -File "test.txt" "this is line 1`nrush is awesome"
                It "returns true if the file contains the specified content" {
                    Test-PositiveAssertion (PesterContain "$TestDrive\test.txt" "rush")
                }
                It "returns true if the file contains the specified content with different case" {
                    Test-PositiveAssertion (PesterContain "$TestDrive\test.txt" "RUSH")
                }
    
                It "returns false if the file does not contain the specified content" {
                    Test-NegativeAssertion (PesterContain "$TestDrive\test.txt" "slime")
                }
            }
        }
    }
    
  • tools\Functions\Assertions\Contain.ps1 Show
    function PesterContain($file, $contentExpecation) {
        return ((Get-Content $file) -match $contentExpecation)
    }
    
    function PesterContainFailureMessage($file, $contentExpecation) {
        return "Expected: file ${file} to contain {$contentExpecation}"
    }
    
    function NotPesterContainFailureMessage($file, $contentExpecation) {
        return "Expected: file {$file} to not contain ${contentExpecation} but it did"
    }
    
    
  • tools\Functions\Assertions\BeNullOrEmpty.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterBeNullOrEmpty" {
            It "should return true if null" {
                Test-PositiveAssertion (PesterBeNullOrEmpty $null)
            }
    
            It "should return true if empty string" {
                Test-PositiveAssertion (PesterBeNullOrEmpty "")
            }
    
            It "should return true if empty array" {
                Test-PositiveAssertion (PesterBeNullOrEmpty @())
            }
        }
    }
    
  • tools\Functions\Assertions\BeNullOrEmpty.ps1 Show
    function PesterBeNullOrEmpty($value) {
        if ($null -eq $value) {
            return $true
        }
        if ([String] -eq $value.GetType()) {
            return [String]::IsNullOrEmpty($value)
        }
        if ($null -ne $value.Count) {
            return $value.Count -lt 1
        }
        return $false
    }
    
    function PesterBeNullOrEmptyFailureMessage($value) {
        return "Expected: value to be empty but it was {$value}"
    }
    
    function NotPesterBeNullOrEmptyFailureMessage {
        return "Expected: value to not be empty"
    }
    
    
  • tools\Functions\Assertions\BeLessThan.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterBeLessThan" {
            It "passes if value Less than expected" {
                Test-PositiveAssertion (PesterBeLessThan 1 2)
                1 | Should BeLessThan 2
            }
            It "fails if values equal" {
                Test-NegativeAssertion (PesterBeLessThan 3 3)
            }
    
            It "fails if value greater than expected" {
                Test-NegativeAssertion (PesterBeLessThan 5 4)
            }
        }
    }
    
  • tools\Functions\Assertions\BeLessThan.ps1 Show
    function PesterBeLessThan($value, $expected)
    {
        return [bool]($value -lt $expected)
    }
    
    function PesterBeLessThanFailureMessage($value,$expected)
    {
        return "Expected {$value} to be less than {$expected}"
    }
    
    function NotPesterBeLessThanFailureMessage($value,$expected)
    {
        return "Expected {$value} to be greater than or equal to {$expected}"
    }
    
  • tools\Functions\Assertions\BeGreaterThan.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "PesterBeGreaterThan" {
            It "passes if value greater than expected" {
                Test-PositiveAssertion (PesterBeGreaterThan 2 1)
                2 | Should BeGreaterThan 1
            }
            It "fails if values equal" {
                Test-NegativeAssertion (PesterBeGreaterThan 3 3)
            }
    
            It "fails if value less than expected" {
                Test-NegativeAssertion (PesterBeGreaterThan 4 5)
            }
        }
    
    }
  • tools\Functions\Context.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    Describe 'Testing Context' {
        It 'Has a non-mandatory fixture parameter which throws the proper error message if missing' {
            $command = Get-Command Context -Module Pester
            $command | Should Not Be $null
    
            $parameter = $command.Parameters['Fixture']
            $parameter | Should Not Be $null
    
            $attribute = $parameter.Attributes | Where-Object { $_.TypeId -eq [System.Management.Automation.ParameterAttribute] }
            $isMandatory = $null -ne $attribute -and $attribute.Mandatory
    
            $isMandatory | Should Be $false
    
            { Context Bogus } | Should Throw 'No test script block is provided'
        }
    }
    
  • tools\Functions\Coverage.ps1 Show
    if ($PSVersionTable.PSVersion.Major -le 2)
    {
        function Exit-CoverageAnalysis { }
        function Get-CoverageReport { }
        function Show-CoverageReport { }
        function Enter-CoverageAnalysis {
            param ( $CodeCoverage )
    
            if ($CodeCoverage) { Write-Error 'Code coverage analysis requires PowerShell 3.0 or later.' }
        }
    
        return
    }
    
    function Enter-CoverageAnalysis
    {
        [CmdletBinding()]
        param (
            [object[]] $CodeCoverage,
            [object] $PesterState
        )
    
        $coverageInfo =
        foreach ($object in $CodeCoverage)
        {
            Get-CoverageInfoFromUserInput -InputObject $object
        }
    
        $PesterState.CommandCoverage = @(Get-CoverageBreakpoints -CoverageInfo $coverageInfo)
    }
    
    function Exit-CoverageAnalysis
    {
        param ([object] $PesterState)
    
        Set-StrictMode -Off
    
        $breakpoints = @($PesterState.CommandCoverage.Breakpoint) -ne $null
        if ($breakpoints.Count -gt 0)
        {
            Remove-PSBreakpoint -Breakpoint $breakpoints
        }
    }
    
    function Get-CoverageInfoFromUserInput
    {
        param (
            [Parameter(Mandatory = $true)]
            [object]
            $InputObject
        )
    
        if ($InputObject -is [System.Collections.IDictionary])
        {
            $unresolvedCoverageInfo = Get-CoverageInfoFromDictionary -Dictionary $InputObject
        }
        else
        {
            $unresolvedCoverageInfo = New-CoverageInfo -Path ([string]$InputObject)
        }
    
        Resolve-CoverageInfo -UnresolvedCoverageInfo $unresolvedCoverageInfo
    }
    
    function New-CoverageInfo
    {
        param ([string] $Path, [string] $Function = $null, [int] $StartLine = 0, [int] $EndLine = 0)
    
        return [pscustomobject]@{
            Path = $Path
            Function = $Function
            StartLine = $StartLine
            EndLine = $EndLine
        }
    }
    
    function Get-CoverageInfoFromDictionary
    {
        param ([System.Collections.IDictionary] $Dictionary)
    
        [string] $path = Get-DictionaryValueFromFirstKeyFound -Dictionary $Dictionary -Key 'Path', 'p'
        if ([string]::IsNullOrEmpty($path))
        {
            throw "Coverage value '$Dictionary' is missing required Path key."
        }
    
        $startLine = Get-DictionaryValueFromFirstKeyFound -Dictionary $Dictionary -Key 'StartLine', 'Start', 's'
        $endLine = Get-DictionaryValueFromFirstKeyFound -Dictionary $Dictionary -Key 'EndLine', 'End', 'e'
        [string] $function = Get-DictionaryValueFromFirstKeyFound -Dictionary $Dictionary -Key 'Function', 'f'
    
        $startLine = Convert-UnknownValueToInt -Value $startLine -DefaultValue 0
        $endLine = Convert-UnknownValueToInt -Value $endLine -DefaultValue 0
    
        return New-CoverageInfo -Path $path -StartLine $startLine -EndLine $endLine -Function $function
    }
    
    function Get-DictionaryValueFromFirstKeyFound
    {
        param ([System.Collections.IDictionary] $Dictionary, [object[]] $Key)
    
        foreach ($keyToTry in $Key)
        {
            if ($Dictionary.Contains($keyToTry)) { return $Dictionary[$keyToTry] }
        }
    }
    
    function Convert-UnknownValueToInt
    {
        param ([object] $Value, [int] $DefaultValue = 0)
    
        try
        {
            return [int] $Value
        }
        catch
        {
            return $DefaultValue
        }
    }
    
    function Resolve-CoverageInfo
    {
        param ([psobject] $UnresolvedCoverageInfo)
    
        $path = $UnresolvedCoverageInfo.Path
    
        try
        {
            $resolvedPaths = Resolve-Path -Path $path -ErrorAction Stop
        }
        catch
        {
            Write-Error "Could not resolve coverage path '$path': $($_.Exception.Message)"
            return
        }
    
        $filePaths =
        foreach ($resolvedPath in $resolvedPaths)
        {
            $item = Get-Item -LiteralPath $resolvedPath
            if ($item -is [System.IO.FileInfo] -and ('.ps1','.psm1') -contains $item.Extension)
            {
                $item.FullName
            }
            elseif (-not $item.PsIsContainer)
            {
                Write-Warning "CodeCoverage path '$path' resolved to a non-PowerShell file '$($item.FullName)'; this path will not be part of the coverage report."
            }
        }
    
        $params = @{
            StartLine = $UnresolvedCoverageInfo.StartLine
            EndLine = $UnresolvedCoverageInfo.EndLine
            Function = $UnresolvedCoverageInfo.Function
        }
    
        foreach ($filePath in $filePaths)
        {
            $params['Path'] = $filePath
            New-CoverageInfo @params
        }
    }
    
    function Get-CoverageBreakpoints
    {
        [CmdletBinding()]
        param (
            [object[]] $CoverageInfo
        )
    
        $fileGroups = @($CoverageInfo | Group-Object -Property Path)
        foreach ($fileGroup in $fileGroups)
        {
            Write-Verbose "Initializing code coverage analysis for file '$($fileGroup.Name)'"
            $totalCommands = 0
            $analyzedCommands = 0
    
            :commandLoop
            foreach ($command in Get-CommandsInFile -Path $fileGroup.Name)
            {
                $totalCommands++
    
                foreach ($coverageInfoObject in $fileGroup.Group)
                {
                    if (Test-CoverageOverlapsCommand -CoverageInfo $coverageInfoObject -Command $command)
                    {
                        $analyzedCommands++
                        New-CoverageBreakpoint -Command $command
                        continue commandLoop
                    }
                }
            }
    
            Write-Verbose "Analyzing $analyzedCommands of $totalCommands commands in file '$($fileGroup.Name)' for code coverage"
        }
    }
    
    function Get-CommandsInFile
    {
        param ([string] $Path)
    
        $errors = $null
        $tokens = $null
        $ast = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref] $tokens, [ref] $errors)
    
        if ($PSVersionTable.PSVersion.Major -ge 5)
        {
            # In PowerShell 5.0, dynamic keywords for DSC configurations are represented by the DynamicKeywordStatementAst
            # class.  They still trigger breakpoints, but are not a child class of CommandBaseAst anymore.
    
            $predicate = {
                $args[0] -is [System.Management.Automation.Language.DynamicKeywordStatementAst] -or
                $args[0] -is [System.Management.Automation.Language.CommandBaseAst]
            }
        }
        else
        {
            $predicate = { $args[0] -is [System.Management.Automation.Language.CommandBaseAst] }
        }
    
        $searchNestedScriptBlocks = $true
        $ast.FindAll($predicate, $searchNestedScriptBlocks)
    }
    
    function Test-CoverageOverlapsCommand
    {
        param ([object] $CoverageInfo, [System.Management.Automation.Language.Ast] $Command)
    
        if ($CoverageInfo.Function)
        {
            Test-CommandInsideFunction -Command $Command -Function $CoverageInfo.Function
        }
        else
        {
            Test-CoverageOverlapsCommandByLineNumber @PSBoundParameters
        }
    
    }
    
    function Test-CommandInsideFunction
    {
        param ([System.Management.Automation.Language.Ast] $Command, [string] $Function)
    
        for ($ast = $Command; $null -ne $ast; $ast = $ast.Parent)
        {
            $functionAst = $ast -as [System.Management.Automation.Language.FunctionDefinitionAst]
            if ($null -ne $functionAst -and $functionAst.Name -like $Function)
            {
                return $true
            }
        }
    
        return $false
    }
    
    function Test-CoverageOverlapsCommandByLineNumber
    {
        param ([object] $CoverageInfo, [System.Management.Automation.Language.Ast] $Command)
    
        $commandStart = $Command.Extent.StartLineNumber
        $commandEnd = $Command.Extent.EndLineNumber
        $coverStart = $CoverageInfo.StartLine
        $coverEnd = $CoverageInfo.EndLine
    
        # An EndLine value of 0 means to cover the entire rest of the file from StartLine
        # (which may also be 0)
        if ($coverEnd -le 0) { $coverEnd = [int]::MaxValue }
    
        return (Test-RangeContainsValue -Value $commandStart -Min $coverStart -Max $coverEnd) -or
               (Test-RangeContainsValue -Value $commandEnd -Min $coverStart -Max $coverEnd)
    }
    
    function Test-RangeContainsValue
    {
        param ([int] $Value, [int] $Min, [int] $Max)
        return $Value -ge $Min -and $Value -le $Max
    }
    
    function New-CoverageBreakpoint
    {
        param ([System.Management.Automation.Language.Ast] $Command)
    
        if (IsIgnoredCommand -Command $Command) { return }
    
        $params = @{
            Script = $Command.Extent.File
            Line   = $Command.Extent.StartLineNumber
            Column = $Command.Extent.StartColumnNumber
            Action = { }
        }
    
        $breakpoint = Set-PSBreakpoint @params
    
        [pscustomobject] @{
            File       = $Command.Extent.File
            Function   = Get-ParentFunctionName -Ast $Command
            Line       = $Command.Extent.StartLineNumber
            Command    = Get-CoverageCommandText -Ast $Command
            Breakpoint = $breakpoint
        }
    }
    
    function IsIgnoredCommand
    {
        param ([System.Management.Automation.Language.Ast] $Command)
    
        if (-not $Command.Extent.File)
        {
            # This can happen if the script contains "configuration" or any similarly implemented
            # dynamic keyword.  PowerShell modifies the script code and reparses it in memory, leading
            # to AST elements with no File in their Extent.
            return $true
        }
    
        if ($PSVersionTable.PSVersion.Major -ge 4)
        {
            if ($Command.Extent.Text -eq 'Configuration')
            {
                # More DSC voodoo.  Calls to "configuration" generate breakpoints, but their HitCount
                # stays zero (even though they are executed.)  For now, ignore them, unless we can come
                # up with a better solution.
                return $true
            }
    
            if (IsChildOfHashtableDynamicKeyword -Command $Command)
            {
                # The lines inside DSC resource declarations don't trigger their breakpooints when executed,
                # just like the "configuration" keyword itself.  I don't know why, at this point, but just like
                # configuration, we'll ignore it so it doesn't clutter up the coverage analysis with useless junk.
                return $true
            }
        }
    
        if (IsClosingLoopCondition -Command $Command)
        {
            # For some reason, the closing expressions of do/while and do/until loops don't trigger their breakpoints.
            # To avoid useless clutter, we'll ignore those lines as well.
            return $true
        }
    
        return $false
    }
    
    function IsChildOfHashtableDynamicKeyword
    {
        param ([System.Management.Automation.Language.Ast] $Command)
    
        for ($ast = $Command.Parent; $null -ne $ast; $ast = $ast.Parent)
        {
            if ($PSVersionTable.PSVersion.Major -ge 5)
            {
                # The ast behaves differently for DSC resources with version 5+.  There's a new DynamicKeywordStatementAst class,
                # and they no longer are represented by CommandAst objects.
    
                if ($ast -is [System.Management.Automation.Language.DynamicKeywordStatementAst] -and
                    $ast.CommandElements[-1] -is [System.Management.Automation.Language.HashtableAst])
                {
                    return $true
                }
            }
            else
            {
                if ($ast -is [System.Management.Automation.Language.CommandAst] -and
                    $null -ne $ast.DefiningKeyword -and
                    $ast.DefiningKeyword.BodyMode -eq [System.Management.Automation.Language.DynamicKeywordBodyMode]::Hashtable)
                {
                    return $true
                }
            }
        }
    
        return $false
    }
    
    function IsClosingLoopCondition
    {
        param ([System.Management.Automation.Language.Ast] $Command)
    
        $ast = $Command
    
        while ($null -ne $ast.Parent)
        {
            if (($ast.Parent -is [System.Management.Automation.Language.DoWhileStatementAst] -or
                $ast.Parent -is [System.Management.Automation.Language.DoUntilStatementAst]) -and
                $ast.Parent.Condition -eq $ast)
            {
                return $true
            }
    
            $ast = $ast.Parent
        }
    
        return $false
    }
    
    function Get-ParentFunctionName
    {
        param ([System.Management.Automation.Language.Ast] $Ast)
    
        $parent = $Ast.Parent
    
        while ($null -ne $parent -and $parent -isnot [System.Management.Automation.Language.FunctionDefinitionAst])
        {
            $parent = $parent.Parent
        }
    
        if ($null -eq $parent)
        {
            return ''
        }
        else
        {
            return $parent.Name
        }
    }
    
    function Get-CoverageCommandText
    {
        param ([System.Management.Automation.Language.Ast] $Ast)
    
        $reportParentExtentTypes = @(
            [System.Management.Automation.Language.ReturnStatementAst]
            [System.Management.Automation.Language.ThrowStatementAst]
            [System.Management.Automation.Language.AssignmentStatementAst]
            [System.Management.Automation.Language.IfStatementAst]
        )
    
        $parent = Get-ParentNonPipelineAst -Ast $Ast
    
        if ($null -ne $parent)
        {
            if ($parent -is [System.Management.Automation.Language.HashtableAst])
            {
                return Get-KeyValuePairText -HashtableAst $parent -ChildAst $Ast
            }
            elseif ($reportParentExtentTypes -contains $parent.GetType())
            {
                return $parent.Extent.Text
            }
        }
    
        return $Ast.Extent.Text
    }
    
    function Get-ParentNonPipelineAst
    {
        param ([System.Management.Automation.Language.Ast] $Ast)
    
        $parent = $null
        if ($null -ne $Ast) { $parent = $Ast.Parent }
    
        while ($parent -is [System.Management.Automation.Language.PipelineAst])
        {
            $parent = $parent.Parent
        }
    
        return $parent
    }
    
    function Get-KeyValuePairText
    {
        param (
            [System.Management.Automation.Language.HashtableAst] $HashtableAst,
            [System.Management.Automation.Language.Ast] $ChildAst
        )
    
        Set-StrictMode -Off
    
        foreach ($keyValuePair in $HashtableAst.KeyValuePairs)
        {
            if ($keyValuePair.Item2.PipelineElements -contains $ChildAst)
            {
                return '{0} = {1}' -f $keyValuePair.Item1.Extent.Text, $keyValuePair.Item2.Extent.Text
            }
        }
    
        # This shouldn't happen, but just in case, default to the old output of just the expression.
        return $ChildAst.Extent.Text
    }
    
    function Get-CoverageMissedCommands
    {
        param ([object[]] $CommandCoverage)
        $CommandCoverage | Where-Object { $_.Breakpoint.HitCount -eq 0 }
    }
    
    function Get-CoverageReport
    {
        param ([object] $PesterState)
    
        $totalCommandCount = $PesterState.CommandCoverage.Count
    
        $missedCommands = @(Get-CoverageMissedCommands -CommandCoverage $PesterState.CommandCoverage | Select-Object File, Line, Function, Command)
        $analyzedFiles = @($PesterState.CommandCoverage | Select-Object -ExpandProperty File -Unique)
        $fileCount = $analyzedFiles.Count
    
        $executedCommandCount = $totalCommandCount - $missedCommands.Count
    
        [pscustomobject] @{
            NumberOfCommandsAnalyzed = $totalCommandCount
            NumberOfFilesAnalyzed    = $fileCount
            NumberOfCommandsExecuted = $executedCommandCount
            NumberOfCommandsMissed   = $missedCommands.Count
            MissedCommands           = $missedCommands
            AnalyzedFiles            = $analyzedFiles
        }
    }
    
    function Show-CoverageReport
    {
        param ([object] $CoverageReport)
    
        if ($null -eq $CoverageReport -or $CoverageReport.NumberOfCommandsAnalyzed -eq 0)
        {
            return
        }
    
        $totalCommandCount = $CoverageReport.NumberOfCommandsAnalyzed
        $fileCount = $CoverageReport.NumberOfFilesAnalyzed
        $executedPercent = ($CoverageReport.NumberOfCommandsExecuted / $CoverageReport.NumberOfCommandsAnalyzed).ToString("P2")
    
        $commandPlural = $filePlural = ''
        if ($totalCommandCount -gt 1) { $commandPlural = 's' }
        if ($fileCount -gt 1) { $filePlural = 's' }
    
        $commonParent = Get-CommonParentPath -Path $CoverageReport.AnalyzedFiles
        $report = $CoverageReport.MissedCommands | Select-Object -Property @(
            @{ Name = 'File'; Expression = { Get-RelativePath -Path $_.File -RelativeTo $commonParent } }
            'Function'
            'Line'
            'Command'
        )
    
        Write-Screen ''
        Write-Screen 'Code coverage report:'
        Write-Screen "Covered $executedPercent of $totalCommandCount analyzed command$commandPlural in $fileCount file$filePlural."
    
        if ($CoverageReport.MissedCommands.Count -gt 0)
        {
            Write-Screen ''
            Write-Screen 'Missed commands:'
            $report | Format-Table -AutoSize | Out-String | Write-Screen
        }
    }
    
    function Get-CommonParentPath
    {
        param ([string[]] $Path)
    
        $pathsToTest = @( $Path | Select-Object -Unique )
    
        if ($pathsToTest.Count -gt 0)
        {
            $parentPath = Split-Path -Path $pathsToTest[0] -Parent
    
            while ($parentPath.Length -gt 0)
            {
                $nonMatches = $pathsToTest -notmatch "^$([regex]::Escape($parentPath))"
    
                if ($nonMatches.Count -eq 0)
                {
                    return $parentPath
                }
                else
                {
                    $parentPath = Split-Path -Path $parentPath -Parent
                }
            }
        }
    
        return [string]::Empty
    }
    
    function Get-RelativePath
    {
        param ( [string] $Path, [string] $RelativeTo )
        return $Path -replace "^$([regex]::Escape($RelativeTo))\\?"
    }
    
  • tools\Functions\Assertions\Be.ps1 Show
    #Be
    function PesterBe($value, $expected) {
        return ($expected -eq $value)
    }
    
    function PesterBeFailureMessage($value, $expected) {
        if (-not (($expected -is [string]) -and ($value -is [string]))) 
        {
            return "Expected: {$expected}`nBut was:  {$value}"
        }
        <#joining the output strings to a single string here, otherwise I get 
           Cannot find an overload for "Exception" and the argument count: "4".
           at line: 63 in C:\Users\nohwnd\github\pester\Functions\Assertions\Should.ps1
           
        This is a quickwin solution, doing the join in the Should directly might be better
        way of doing this. But I don't want to mix two problems.
        #>
        ( Get-CompareStringMessage -Expected $expected -Actual $value ) -join "`n"
    }
    
    function NotPesterBeFailureMessage($value, $expected) {
        return "Expected: value was {$value}, but should not have been the same"
    }
    
    #BeExactly
    function PesterBeExactly($value, $expected) {
        return ($expected -ceq $value)
    }
    
    function PesterBeExactlyFailureMessage($value, $expected) {
        if (-not (($expected -is [string]) -and ($value -is [string]))) 
        {
            return "Expected exactly: {$expected}`nBut was: {$value}"
        }
        <#joining the output strings to a single string here, otherwise I get 
           Cannot find an overload for "Exception" and the argument count: "4".
           at line: 63 in C:\Users\nohwnd\github\pester\Functions\Assertions\Should.ps1
           
        This is a quickwin solution, doing the join in the Should directly might be better
        way of doing this. But I don't want to mix two problems.
        #>
        ( Get-CompareStringMessage -Expected $expected -Actual $value -CaseSensitive ) -join "`n"
    }
    
    function NotPesterBeExactlyFailureMessage($value, $expected) {
        return "Expected: value was {$value}, but should not have been exactly the same"
    }
    
    #common functions
    function Get-CompareStringMessage {
        param(
            [Parameter(Mandatory=$true)]
            [String]$Expected, 
            [Parameter(Mandatory=$true)]
            [String]$Actual,
            [switch]$CaseSensitive
        )
        
        $expectedLength = $expected.Length
        $actualLength = $actual.Length
        $maxLength = $expectedLength,$actualLength | Sort -Descending | select -First 1
        
        $differenceIndex = $null 
        for ($i = 0; $i -lt $maxLength -and ($null -eq $differenceIndex); ++$i){
            $differenceIndex = if ($CaseSensitive -and ($expected[$i] -cne $actual[$i])) 
            {
                $i
            }
            elseif ($expected[$i] -ne $actual[$i]) 
            {
                $i
            }     
        }
        
        [string]$output = $null
        if ($null -ne $differenceIndex)
        {   
            if ($expected.Length -ne $actual.Length) {
               "Expected string length $expectedLength but was $actualLength. Strings differ at index $differenceIndex."
            }
            else
            {
               "String lengths are both $expectedLength. Strings differ at index $differenceIndex."
            }
            
            
            "Expected: {{{0}}}" -f ( $expected | Expand-SpecialCharacters )
            "But was:  {{{0}}}" -f ( $actual | Expand-SpecialCharacters )
            
            $specialCharacterOffset = $null
            if ($differenceIndex -ne 0)
            {
                #count all the special characters before the difference
                $specialCharacterOffset = ($actual[0..($differenceIndex-1)] | 
                    Where {"`n","`r","`t","`b","`0" -contains $_} | 
                    Measure-Object | 
                    select -ExpandProperty Count)
            }
    
            '-'*($differenceIndex+$specialCharacterOffset+11)+'^'
        }
    }
    
    function Expand-SpecialCharacters {
        param (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$InputObject) 
        process {
            $InputObject -replace "`n","\n" -replace "`r","\r" -replace "`t","\t" -replace "`0", "\0" -replace "`b","\b"
        }
    }
    
    
  • tools\Examples\Validator\Validator.Tests.ps1 Show
    function MyValidator($thing_to_validate) {
        return $thing_to_validate.StartsWith("s")
    }
    
    function Invoke-SomethingThatUsesMyValidator {
        param(
            [ValidateScript({MyValidator $_})]
            $some_param
        )
    }
    
    Describe "Testing a validator" {
    
        It "calls MyValidator" {
            Mock MyValidator -MockWith { return $true }
            Invoke-SomethingThatUsesMyValidator "test"
            $was_called_once = 1
            Assert-MockCalled MyValidator $was_called_once
        }
    
    }
    
    Describe "MyValidator" {
    
        It "passes things that start with the letter S" {
            $result = MyValidator "summer"
            $result | Should Be $true
        }
    
        It "does not pass a param that does not start with S" {
            $result = MyValidator "bummer"
            $result | Should Be $false
        }
    }
    
    
  • tools\Examples\Calculator\Add-Numbers.Tests.ps1 Show
    $here = Split-Path -Parent $MyInvocation.MyCommand.Path
    . "$here\Add-Numbers.ps1"
    
    Describe -Tags "Example" "Add-Numbers" {
    
        It "adds positive numbers" {
            Add-Numbers 2 3 | Should Be 5
        }
    
        It "adds negative numbers" {
            Add-Numbers (-2) (-2) | Should Be (-4)
        }
    
        It "adds one negative number to positive number" {
            Add-Numbers (-2) 2 | Should Be 0
        }
    
        It "concatenates strings if given strings" {
            Add-Numbers two three | Should Be "twothree"
        }
    
        It "should not be 0" {
            Add-Numbers 2 3 | Should Not Be 0
        }
    }
    
    
  • tools\Examples\Calculator\Add-Numbers.ps1 Show
    function Add-Numbers($a, $b) {
        return $a + $b
    }
    
  • tools\en-US\about_TestDrive.help.txt Show
    TOPIC
    	TestDrive
    
    SYNOPSIS
    	A PSDrive for file activity limited to the scope of a singe Describe or 
    	Context block.
    
    DESCRIPTION	
    	A test may need to work with file operations and validate certain tyes of 
    	file activities. It is usually desirable not to perform file activity tests 
    	that will produce side effects outside of an individual test. Pester 
    	creates a PSDrive inside the user's temporary drive that is accesible via a 
    	names PSDrive TestDrive:. Pester will remove this drive after the test 
    	completes. You may use this drive to isolate the file operations of your 
    	test to a temporary store.
    
    EXAMPLE
    	function Add-Footer($path, $footer) {
    	    Add-Content $path -Value $footer
    	}
    
    	Describe "Add-Footer" {
    	    $testPath="TestDrive:\test.txt"
    	    Set-Content $testPath -value "my test text."
    	    Add-Footer $testPath "-Footer"
    	    $result = Get-Content $testPath
    
    	    It "adds a footer" {
    	        (-join $result).Should.Be("my test text.-Footer")
    	    }
    	}
    	
    	When this test completes, the contents of the TestDrive PSDrive will 
    	be removed.
    
    SEE ALSO
    	Context
    	Describe
    	It
    	about_Should
    
  • tools\Functions\Describe.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    Describe 'Testing Describe' {
        It 'Has a non-mandatory fixture parameter which throws the proper error message if missing' {
            $command = Get-Command Describe -Module Pester
            $command | Should Not Be $null
    
            $parameter = $command.Parameters['Fixture']
            $parameter | Should Not Be $null
    
            $attribute = $parameter.Attributes | Where-Object { $_.TypeId -eq [System.Management.Automation.ParameterAttribute] }
            $isMandatory = $null -ne $attribute -and $attribute.Mandatory
    
            $isMandatory | Should Be $false
    
            { Describe Bogus } | Should Throw 'No test script block is provided'
        }
    }
    
  • tools\Functions\InModuleScope.ps1 Show
    function InModuleScope
    {
    <#
    .SYNOPSIS
       Allows you to execute parts of a test script within the
       scope of a PowerShell script module.
    .DESCRIPTION
       By injecting some test code into the scope of a PowerShell
       script module, you can use non-exported functions, aliases
       and variables inside that module, to perform unit tests on 
       its internal implementation.
    
       InModuleScope may be used anywhere inside a Pester script,
       either inside or outside a Describe block.
    .PARAMETER ModuleName
       The name of the module into which the test code should be
       injected. This module must already be loaded into the current
       PowerShell session.
    .PARAMETER ScriptBlock
       The code to be executed within the script module.
    .EXAMPLE
        # The script module:
        function PublicFunction
        {
            # Does something
        }
    
        function PrivateFunction
        {
            return $true
        }
    
        Export-ModuleMember -Function PublicFunction
    
        # The test script:
    
        Import-Module MyModule
    
        InModuleScope MyModule {
            Describe 'Testing MyModule' {
                It 'Tests the Private function' {
                    PrivateFunction | Should Be $true
                }
            }
        }
    
        Normally you would not be able to access "PrivateFunction" from
        the powershell session, because the module only exported
        "PublicFunction".  Using InModuleScope allowed this call to
        "PrivateFunction" to work successfully.
    #>
    
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [string]
            $ModuleName,
    
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock
        )
    
        if ($null -eq (Get-Variable -Name Pester -ValueOnly -ErrorAction (Get-IgnoreErrorPreference)))
        {
            # User has executed a test script directly instead of calling Invoke-Pester
            $Pester = New-PesterState -Path (Resolve-Path .) -TestNameFilter $null -TagFilter @() -ExcludeTagFilter @() -SessionState $PSCmdlet.SessionState
            $script:mockTable = @{}
        }
    
        try
        {
            $module = Get-Module -Name $ModuleName -All -ErrorAction Stop
        }
        catch
        {
            throw "No module named '$ModuleName' is currently loaded."
        }
    
        $originalState = $Pester.SessionState
        $originalScriptBlockScope = Get-ScriptBlockScope -ScriptBlock $ScriptBlock
    
        try
        {
            $Pester.SessionState = $module.SessionState
    
            Set-ScriptBlockScope -ScriptBlock $ScriptBlock -SessionState $module.SessionState
    
            & $ScriptBlock
        }
        finally
        {
            $Pester.SessionState = $originalState
            Set-ScriptBlockScope -ScriptBlock $ScriptBlock -SessionStateInternal $originalScriptBlockScope 
        }
    }
    
  • tools\Functions\It.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe 'Get-PesterResult' {
            It 'records the correct stack line number of failed tests' {
                #the $script scriptblock below is used as a position marker to determine
                #on which line the test failed.
                try{'something' | should be 'nothing'}catch{ $ex=$_} ; $script={}
                $result = Get-PesterResult $script 0 $ex
                $result.Stacktrace | should match "at line: $($script.startPosition.StartLine) in "
            }
        }
    
        Describe 'It - Implementation' {
            $testState = New-PesterState -Path $TestDrive
    
            It 'Throws an error if It is called outside of Describe' {
                $scriptBlock = { ItImpl -Pester $testState 'Tries to enter a test without entering a Describe first' { } }
                $scriptBlock | Should Throw 'The It command may only be used inside a Describe block.'
            }
    
            $testState.EnterDescribe('Mocked Describe')
    
            # We call EnterTest() directly here because if we actually nest calls to ItImpl, the outer call will catch the error we're trying to
            # verify with Should Throw.  (Another option would be to nest the ItImpl calls, and look for a failed test result in $testState.)
            $testState.EnterTest('Outer Test')
    
            It 'Throws an error if you try to enter It from inside another It' {
                $scriptBlock = {
                    ItImpl -Pester $testState 'Enters the second It' { }
                }
    
                $scriptBlock | Should Throw 'You already are in It, you cannot enter It twice'
            }
    
            $testState.LeaveTest()
    
            It 'Throws an error if you fail to pass in a test block' {
                $scriptBlock = { ItImpl -Pester $testState 'Some Name' }
                $scriptBlock | Should Throw 'No test script block is provided. (Have you put the open curly brace on the next line?)'
            }
    
            It 'Does not throw an error if It is called inside a Describe, and adds a successful test result.' {
                $scriptBlock = { ItImpl -Pester $testState 'Enters an It block inside a Describe' { } }
                $scriptBlock | Should Not Throw
    
                $testState.TestResult[-1].Passed | Should Be $true
                $testState.TestResult[-1].ParameterizedSuiteName | Should BeNullOrEmpty
            }
    
            It 'Does not throw an error if the -Pending switch is used, and no script block is passed' {
                $scriptBlock = { ItImpl -Pester $testState 'Some Name' -Pending }
                $scriptBlock | Should Not Throw
            }
    
            It 'Does not throw an error if the -Skip switch is used, and no script block is passed' {
                $scriptBlock = { ItImpl -Pester $testState 'Some Name' -Skip }
                $scriptBlock | Should Not Throw
            }
    
            It 'Creates a pending test for an empty (whitespace and comments only) script block' {
                $scriptBlock = {
                    # Single-Line comment
                    <#
                        Multi-
                        Line-
                        Comment
                    #>
                }
    
                { ItImpl -Pester $testState 'Some Name' $scriptBlock } | Should Not Throw
                $testState.TestResult[-1].Result | Should Be 'Pending'
            }
    
            It 'Adds a failed test if the script block throws an exception' {
                $scriptBlock = {
                    ItImpl -Pester $testState 'Enters an It block inside a Describe' {
                        throw 'I am a failed test'
                    }
                }
    
                $scriptBlock | Should Not Throw
                $testState.TestResult[-1].Passed | Should Be $false
                $testState.TestResult[-1].ParameterizedSuiteName | Should BeNullOrEmpty
                $testState.TestResult[-1].FailureMessage | Should Be 'I am a failed test'
            }
    
            $script:counterNameThatIsReallyUnlikelyToConflictWithAnything = 0
    
            It 'Calls the output script block for each test' {
                $outputBlock = { $script:counterNameThatIsReallyUnlikelyToConflictWithAnything++ }
    
                ItImpl -Pester $testState 'Does something' -OutputScriptBlock $outputBlock { }
                ItImpl -Pester $testState 'Does something' -OutputScriptBlock $outputBlock { }
                ItImpl -Pester $testState 'Does something' -OutputScriptBlock $outputBlock { }
    
                $script:counterNameThatIsReallyUnlikelyToConflictWithAnything | Should Be 3
            }
    
            Remove-Variable -Scope Script -Name counterNameThatIsReallyUnlikelyToConflictWithAnything
    
            Context 'Parameterized Tests' {
                # be careful about variable naming here; with InModuleScope Pester, we can create the same types of bugs that the v3
                # scope isolation fixed for everyone else.  (Naming this variable $testCases gets hidden later by parameters of the
                # same name in It.)
    
                $cases = @(
                    @{ a = 1; b = 1; expectedResult = 2}
                    @{ a = 1; b = 2; expectedResult = 3}
                    @{ a = 5; b = 4; expectedResult = 9}
                    @{ a = 1; b = 1; expectedResult = 'Intentionally failed' }
                )
    
                $suiteName = 'Adds <a> and <b> to get <expectedResult>.  <Bogus> is not a parameter.'
    
                ItImpl -Pester $testState -Name $suiteName -TestCases $cases {
                    param ($a, $b, $expectedResult)
    
                    ($a + $b) | Should Be $expectedResult
                }
    
                It 'Creates test result records with the ParameterizedSuiteName property set' {
                    for ($i = -1; $i -ge -4; $i--)
                    {
                        $testState.TestResult[$i].ParameterizedSuiteName | Should Be $suiteName
                    }
                }
    
                It 'Expands parameters in parameterized test suite names' {
                    for ($i = -1; $i -ge -4; $i--)
                    {
                        $expectedName = "Adds $($cases[$i]['a']) and $($cases[$i]['b']) to get $($cases[$i]['expectedResult']).  <Bogus> is not a parameter."
                        $testState.TestResult[$i].Name | Should Be $expectedName
                    }
                }
    
                It 'Logs the proper successes and failures' {
                    $testState.TestResult[-1].Passed | Should Be $false
                    for ($i = -2; $i -ge -4; $i--)
                    {
                        $testState.TestResult[$i].Passed | Should Be $true
                    }
                }
            }
        }
    
        Describe 'Get-OrderedParameterDictionary' {
            $_testScriptBlock = {
                param (
                    $1, $c, $0, $z, $a, ${Something.Really/Weird }
                )
            }
    
            $hashtable = @{
                '1' = 'One'
                '0' = 'Zero'
                z = 'Z'
                a = 'A'
                c = 'C'
                'Something.Really/Weird ' = 'Weird'
            }
    
            $dictionary = Get-OrderedParameterDictionary -ScriptBlock $_testScriptBlock -Dictionary $hashtable
    
            It 'Reports keys and values in the same order as the param block' {
                ($dictionary.Keys -join ',') |
                Should Be '1,c,0,z,a,Something.Really/Weird '
    
                ($dictionary.Values -join ',') |
                Should Be 'One,C,Zero,Z,A,Weird'
            }
        }
    
        Describe 'Remove-Comments' {
            It 'Removes single line comments' {
                Remove-Comments -Text 'code #comment' | Should Be 'code '
            }
            It 'Removes multi line comments' {
                Remove-Comments -Text 'code <#comment
                comment#> code' | Should Be 'code  code'
            }
        }
    }
    
  • tools\Functions\Mock.ps1 Show
    function Mock {
    
    <#
    .SYNOPSIS
    Mocks the behavior of an existing command with an alternate
    implementation.
    
    .DESCRIPTION
    This creates new behavior for any existing command within the scope of a
    Describe or Context block. The function allows you to specify a script block
    that will become the command's new behavior.
    
    Optionally, you may create a Parameter Filter which will examine the
    parameters passed to the mocked command and will invoke the mocked
    behavior only if the values of the parameter values pass the filter. If
    they do not, the original command implementation will be invoked instead
    of a mock.
    
    You may create multiple mocks for the same command, each using a different
    ParameterFilter. ParameterFilters will be evaluated in reverse order of
    their creation. The last one created will be the first to be evaluated.
    The mock of the first filter to pass will be used. The exception to this
    rule are Mocks with no filters. They will always be evaluated last since
    they will act as a "catch all" mock.
    
    Mocks can be marked Verifiable. If so, the Assert-VerifiableMocks command
    can be used to check if all Verifiable mocks were actually called. If any
    verifiable mock is not called, Assert-VerifiableMocks will throw an
    exception and indicate all mocks not called.
    
    If you wish to mock commands that are called from inside a script module,
    you can do so by using the -ModuleName parameter to the Mock command. This
    injects the mock into the specified module. If you do not specify a
    module name, the mock will be created in the same scope as the test script.
    You may mock the same command multiple times, in different scopes, as needed.
    Each module's mock maintains a separate call history and verified status.
    
    .PARAMETER CommandName
    The name of the command to be mocked.
    
    .PARAMETER MockWith
    A ScriptBlock specifying the behvior that will be used to mock CommandName.
    The default is an empty ScriptBlock.
    NOTE: Do not specify param or dynamicparam blocks in this script block.
    These will be injected automatically based on the signature of the command
    being mocked, and the MockWith script block can contain references to the
    mocked commands parameter variables.
    
    .PARAMETER Verifiable
    When this is set, the mock will be checked when Assert-VerifiableMocks is
    called.
    
    .PARAMETER ParameterFilter
    An optional filter to limit mocking behavior only to usages of
    CommandName where the values of the parameters passed to the command
    pass the filter.
    
    This ScriptBlock must return a boolean value. See examples for usage.
    
    .PARAMETER ModuleName
    Optional string specifying the name of the module where this command
    is to be mocked.  This should be a module that _calls_ the mocked
    command; it doesn't necessarily have to be the same module which
    originally implemented the command.
    
    .EXAMPLE
    Mock Get-ChildItem { return @{FullName = "A_File.TXT"} }
    
    Using this Mock, all calls to Get-ChildItem will return a hashtable with a
    FullName property returning "A_File.TXT"
    
    .EXAMPLE
    Mock Get-ChildItem { return @{FullName = "A_File.TXT"} } -ParameterFilter { $Path -and $Path.StartsWith($env:temp) }
    
    This Mock will only be applied to Get-ChildItem calls within the user's temp directory.
    
    .EXAMPLE
    Mock Set-Content {} -Verifiable -ParameterFilter { $Path -eq "some_path" -and $Value -eq "Expected Value" }
    
    When this mock is used, if the Mock is never invoked and Assert-VerifiableMocks is called, an exception will be thrown. The command behavior will do nothing since the ScriptBlock is empty.
    
    .EXAMPLE
    Mock Get-ChildItem { return @{FullName = "A_File.TXT"} } -ParameterFilter { $Path -and $Path.StartsWith($env:temp\1) }
    Mock Get-ChildItem { return @{FullName = "B_File.TXT"} } -ParameterFilter { $Path -and $Path.StartsWith($env:temp\2) }
    Mock Get-ChildItem { return @{FullName = "C_File.TXT"} } -ParameterFilter { $Path -and $Path.StartsWith($env:temp\3) }
    
    Multiple mocks of the same command may be used. The parameter filter determines which is invoked. Here, if Get-ChildItem is called on the "2" directory of the temp folder, then B_File.txt will be returned.
    
    .EXAMPLE
    Mock Get-ChildItem { return @{FullName="B_File.TXT"} } -ParameterFilter { $Path -eq "$env:temp\me" }
    Mock Get-ChildItem { return @{FullName="A_File.TXT"} } -ParameterFilter { $Path -and $Path.StartsWith($env:temp) }
    
    Get-ChildItem $env:temp\me
    
    Here, both mocks could apply since both filters will pass. A_File.TXT will be returned because it was the most recent Mock created.
    
    .EXAMPLE
    Mock Get-ChildItem { return @{FullName = "B_File.TXT"} } -ParameterFilter { $Path -eq "$env:temp\me" }
    Mock Get-ChildItem { return @{FullName = "A_File.TXT"} }
    
    Get-ChildItem c:\windows
    
    Here, A_File.TXT will be returned. Since no filter was specified, it will apply to any call to Get-ChildItem that does not pass another filter.
    
    .EXAMPLE
    Mock Get-ChildItem { return @{FullName = "B_File.TXT"} } -ParameterFilter { $Path -eq "$env:temp\me" }
    Mock Get-ChildItem { return @{FullName = "A_File.TXT"} }
    
    Get-ChildItem $env:temp\me
    
    Here, B_File.TXT will be returned. Even though the filterless mock was created more recently. This illustrates that filterless Mocks are always evaluated last regardlss of their creation order.
    
    .EXAMPLE
    Mock Get-ChildItem { return @{FullName = "A_File.TXT"} } -ModuleName MyTestModule
    
    Using this Mock, all calls to Get-ChildItem from within the MyTestModule module
    will return a hashtable with a FullName property returning "A_File.TXT"
    
    .EXAMPLE
    Get-Module -Name ModuleMockExample | Remove-Module
    New-Module -Name ModuleMockExample  -ScriptBlock {
        function Hidden { "Internal Module Function" }
        function Exported { Hidden }
    
        Export-ModuleMember -Function Exported
    } | Import-Module -Force
    
    Describe "ModuleMockExample" {
    
        It "Hidden function is not directly accessible outside the module" {
            { Hidden } | Should Throw
        }
    
        It "Original Hidden function is called" {
            Exported | Should Be "Internal Module Function"
        }
    
        It "Hidden is replaced with our implementation" {
            Mock Hidden { "Mocked" } -ModuleName ModuleMockExample
            Exported | Should Be "Mocked"
        }
    }
    
    This example shows how calls to commands made from inside a module can be
    mocked by using the -ModuleName parameter.
    
    
    .LINK
    Assert-MockCalled
    Assert-VerifiableMocks
    Describe
    Context
    It
    about_Should
    about_Mocking
    #>
    
        param(
            [string]$CommandName,
            [ScriptBlock]$MockWith={},
            [switch]$Verifiable,
            [ScriptBlock]$ParameterFilter = {$True},
            [string]$ModuleName
        )
    
        Assert-DescribeInProgress -CommandName Mock
    
        $contextInfo = Validate-Command $CommandName $ModuleName
        $CommandName = $contextInfo.Command.Name
    
        if ($contextInfo.Session.Module -and $contextInfo.Session.Module.Name)
        {
            $ModuleName = $contextInfo.Session.Module.Name
        }
        else
        {
            $ModuleName = ''
        }
    
        $mockWithCopy = [scriptblock]::Create($MockWith.ToString())
        Set-ScriptBlockScope -ScriptBlock $mockWithCopy -SessionState $contextInfo.Session
    
        $block = @{
            Mock       = $mockWithCopy
            Filter     = $ParameterFilter
            Verifiable = $Verifiable
            Scope      = Get-ScopeForMock -PesterState $pester
        }
    
        $mock = $mockTable["$ModuleName||$CommandName"]
    
        if (-not $mock)
        {
            $metadata                = $null
            $cmdletBinding           = ''
            $paramBlock              = ''
            $dynamicParamBlock       = ''
            $dynamicParamScriptBlock = $null
    
            if ($contextInfo.Command.psobject.Properties['ScriptBlock'] -or $contextInfo.Command.CommandType -eq 'Cmdlet')
            {
                $metadata = [System.Management.Automation.CommandMetaData]$contextInfo.Command
                $null = $metadata.Parameters.Remove('Verbose')
                $null = $metadata.Parameters.Remove('Debug')
                $null = $metadata.Parameters.Remove('ErrorAction')
                $null = $metadata.Parameters.Remove('WarningAction')
                $null = $metadata.Parameters.Remove('ErrorVariable')
                $null = $metadata.Parameters.Remove('WarningVariable')
                $null = $metadata.Parameters.Remove('OutVariable')
                $null = $metadata.Parameters.Remove('OutBuffer')
    
                $cmdletBinding = [Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($metadata)
                $paramBlock    = [Management.Automation.ProxyCommand]::GetParamBlock($metadata)
    
                if ($contextInfo.Command.CommandType -eq 'Cmdlet')
                {
                    $dynamicParamBlock = "dynamicparam { Get-MockDynamicParameters -CmdletName '$($contextInfo.Command.Name)' -Parameters `$PSBoundParameters }"
                }
                else
                {
                    $metadataWithoutMandatory = [System.Management.Automation.CommandMetaData]$contextInfo.Command
                    foreach ($parameter in $metadataWithoutMandatory.Parameters.Values)
                    {
                        foreach ($parameterSet in $parameter.ParameterSets.Values)
                        {
                            $parameterSet.IsMandatory = $false
                        }
                    }
    
                    $paramBlockWithoutMandatory = [System.Management.Automation.ProxyCommand]::GetParamBlock($metadataWithoutMandatory)
    
                    $dynamicParamBlock = "dynamicparam { Get-MockDynamicParameters -ModuleName '$ModuleName' -FunctionName '$CommandName' -Parameters `$PSBoundParameters }"
    
                    $dynamicParamStatements = Get-DynamicParamBlock -ScriptBlock $contextInfo.Command.ScriptBlock
                    $dynamicParamScriptBlock = [scriptblock]::Create("$cmdletBinding`r`nparam( $paramBlockWithoutMandatory )`r`n$dynamicParamStatements")
    
                    $sessionStateInternal = Get-ScriptBlockScope -ScriptBlock $contextInfo.Command.ScriptBlock
    
                    if ($null -ne $sessionStateInternal)
                    {
                        Set-ScriptBlockScope -ScriptBlock $dynamicParamScriptBlock -SessionStateInternal $sessionStateInternal
                    }
                }
            }
    
            $newContent = Microsoft.PowerShell.Management\Get-Content function:\MockPrototype
            $mockScript = [scriptblock]::Create("$cmdletBinding`r`nparam( $paramBlock )`r`n$dynamicParamBlock`r`nprocess{`r`n$newContent}")
    
            $mock = @{
                OriginalCommand         = $contextInfo.Command
                Blocks                  = @()
                CommandName             = $CommandName
                SessionState            = $contextInfo.Session
                Scope                   = $pester.Scope
                Metadata                = $metadata
                CallHistory             = @()
                DynamicParamScriptBlock = $dynamicParamScriptBlock
            }
    
            $mockTable["$ModuleName||$CommandName"] = $mock
    
            if ($contextInfo.Command.CommandType -eq 'Function')
            {
                $scriptBlock =
                {
                    if ($ExecutionContext.InvokeProvider.Item.Exists("Function:\$args"))
                    {
                        $ExecutionContext.InvokeProvider.Item.Rename("Function:\$args", "script:PesterIsMocking_$args", $true)
                    }
                }
    
                $null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $CommandName
            }
    
            $scriptBlock = { $ExecutionContext.InvokeProvider.Item.Set("Function:\script:$($args[0])", $args[1], $true, $true) }
            $null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $CommandName, $mockScript
        }
    
        $mock.Blocks = @(
            $mock.Blocks | Where-Object { $_.Filter.ToString() -eq '$True' }
            if ($block.Filter.ToString() -eq '$True') { $block }
    
            $mock.Blocks | Where-Object { $_.Filter.ToString() -ne '$True' }
            if ($block.Filter.ToString() -ne '$True') { $block }
        )
    }
    
    
    function Assert-VerifiableMocks {
    <#
    .SYNOPSIS
    Checks if any Verifiable Mock has not been invoked. If so, this will throw an exception.
    
    .DESCRIPTION
    This can be used in tandem with the -Verifiable switch of the Mock
    function. Mock can be used to mock the behavior of an existing command
    and optionally take a -Verifiable switch. When Assert-VerifiableMocks
    is called, it checks to see if any Mock marked Verifiable has not been
    invoked. If any mocks have been found that specified -Verifiable and
    have not been invoked, an exception will be thrown.
    
    .EXAMPLE
    Mock Set-Content {} -Verifiable -ParameterFilter {$Path -eq "some_path" -and $Value -eq "Expected Value"}
    
    { ...some code that never calls Set-Content some_path -Value "Expected Value"... }
    
    Assert-VerifiableMocks
    
    This will throw an exception and cause the test to fail.
    
    .EXAMPLE
    Mock Set-Content {} -Verifiable -ParameterFilter {$Path -eq "some_path" -and $Value -eq "Expected Value"}
    
    Set-Content some_path -Value "Expected Value"
    
    Assert-VerifiableMocks
    
    This will not throw an exception because the mock was invoked.
    
    #>
        Assert-DescribeInProgress -CommandName Assert-VerifiableMocks
    
        [email protected]{}
        $mockTable.Keys | % {
            $m=$_; $mockTable[$m].blocks | ? { $_.Verifiable } | % { $unVerified[$m]=$_ }
        }
        if($unVerified.Count -gt 0) {
            foreach($mock in $unVerified.Keys){
                $array = $mock -split '\|\|'
                $function = $array[1]
                $module = $array[0]
    
                $message = "`r`n Expected $function "
                if ($module) { $message += "in module $module " }
                $message += "to be called with $($unVerified[$mock].Filter)"
            }
            throw $message
        }
    }
    
    function Assert-MockCalled {
    <#
    .SYNOPSIS
    Checks if a Mocked command has been called a certain number of times
    and throws an exception if it has not.
    
    .DESCRIPTION
    This command verifies that a mocked command has been called a certain number
    of times.  If the call history of the mocked command does not match the parameters
    passed to Assert-MockCalled, Assert-MockCalled will throw an exception.
    
    .PARAMETER CommandName
    The mocked command whose call history should be checked.
    
    .PARAMETER ModuleName
    The module where the mock being checked was injected.  This is optional,
    and must match the ModuleName that was used when setting up the Mock.
    
    .PARAMETER Times
    The number of times that the mock must be called to avoid an exception
    from throwing.
    
    .PARAMETER Exactly
    If this switch is present, the number specified in Times must match
    exactly the number of times the mock has been called. Otherwise it
    must match "at least" the number of times specified.  If the value
    passed to the Times parameter is zero, the Exactly switch is implied.
    
    .PARAMETER ParameterFilter
    An optional filter to qualify wich calls should be counted. Only those
    calls to the mock whose parameters cause this filter to return true
    will be counted.
    
    .PARAMETER Scope
    An optional parameter specifying the Pester scope in which to check for
    calls to the mocked command.  By default, Assert-MockCalled will find
    all calls to the mocked command in the current Context block (if present),
    or the current Describe block (if there is no active Context.)  Valid
    values are Describe, Context and It. If you use a scope of Describe or
    Context, the command will identify all calls to the mocked command in the
    current Describe / Context block, as well as all child scopes of that block.
    
    .EXAMPLE
    C:\PS>Mock Set-Content {}
    
    {... Some Code ...}
    
    C:\PS>Assert-MockCalled Set-Content
    
    This will throw an exception and cause the test to fail if Set-Content is not called in Some Code.
    
    .EXAMPLE
    C:\PS>Mock Set-Content -parameterFilter {$path.StartsWith("$env:temp\")}
    
    {... Some Code ...}
    
    C:\PS>Assert-MockCalled Set-Content 2 { $path -eq "$env:temp\test.txt" }
    
    This will throw an exception if some code calls Set-Content on $path=$env:temp\test.txt less than 2 times
    
    .EXAMPLE
    C:\PS>Mock Set-Content {}
    
    {... Some Code ...}
    
    C:\PS>Assert-MockCalled Set-Content 0
    
    This will throw an exception if some code calls Set-Content at all
    
    .EXAMPLE
    C:\PS>Mock Set-Content {}
    
    {... Some Code ...}
    
    C:\PS>Assert-MockCalled Set-Content -Exactly 2
    
    This will throw an exception if some code does not call Set-Content Exactly two times.
    
    .EXAMPLE
    Describe 'Assert-MockCalled Scope behavior' {
        Mock Set-Content { }
    
        It 'Calls Set-Content at least once in the It block' {
            {... Some Code ...}
    
            Assert-MockCalled Set-Content -Exactly 0 -Scope It
        }
    }
    
    Checks for calls only within the current It block.
    
    .EXAMPLE
    Describe 'Describe' {
        Mock -ModuleName SomeModule Set-Content { }
    
        {... Some Code ...}
    
        It 'Calls Set-Content at least once in the Describe block' {
            Assert-MockCalled -ModuleName SomeModule Set-Content
        }
    }
    
    Checks for calls to the mock within the SomeModule module.  Note that both the Mock
    and Assert-MockCalled commands use the same module name.
    
    .NOTES
    The parameter filter passed to Assert-MockCalled does not necessarily have to match the parameter filter
    (if any) which was used to create the Mock.  Assert-MockCalled will find any entry in the command history
    which matches its parameter filter, regardless of how the Mock was created.  However, if any calls to the
    mocked command are made which did not match any mock's parameter filter (resulting in the original command
    being executed instead of a mock), these calls to the original command are not tracked in the call history.
    In other words, Assert-MockCalled can only be used to check for calls to the mocked implementation, not
    to the original.
    
    #>
    
    [CmdletBinding()]
    param(
        [string]$CommandName,
        [switch]$Exactly,
        [int]$Times=1,
        [ScriptBlock]$ParameterFilter = {$True},
        [string] $ModuleName,
    
        [ValidateSet('Describe','Context','It')]
        [string] $Scope
    )
    
        Assert-DescribeInProgress -CommandName Assert-MockCalled
    
        if (-not $PSBoundParameters.ContainsKey('ModuleName') -and $null -ne $pester.SessionState.Module)
        {
            $ModuleName = $pester.SessionState.Module.Name
        }
    
        $mock = $script:mockTable["$ModuleName||$commandName"]
    
        $moduleMessage = ''
        if ($ModuleName)
        {
            $moduleMessage = " in module $ModuleName"
        }
    
        if (-not $mock)
        {
            throw "You did not declare a mock of the $commandName Command${moduleMessage}."
        }
    
        if (-not $Scope)
        {
            if ($pester.CurrentContext)
            {
                $Scope = 'Context'
            }
            else
            {
                $Scope = 'Describe'
            }
        }
    
        $qualifiedCalls = @(
            $mock.CallHistory |
            Where-Object {
                $params = @{
                    ScriptBlock     = $ParameterFilter
                    BoundParameters = $_.BoundParams
                    ArgumentList    = $_.Args
                    Metadata        = $mock.Metadata
                }
    
                (Test-MockCallScope -CallScope $_.Scope -DesiredScope $Scope) -and (Test-ParameterFilter @params)
            }
        )
    
        if($qualifiedCalls.Length -ne $times -and ($Exactly -or ($times -eq 0))) {
            throw "Expected ${commandName}${$moduleMessage} to be called $times times exactly but was called $($qualifiedCalls.Length.ToString()) times"
        } elseif($qualifiedCalls.Length -lt $times) {
            throw "Expected ${commandName}${moduleMessage} to be called at least $times times but was called $($qualifiedCalls.Length) times"
        }
    }
    
    function Test-MockCallScope
    {
        [CmdletBinding()]
        param (
            [string] $CallScope,
            [string] $DesiredScope
        )
    
        # It would probably be cleaner to replace all of these scope strings with an enumerated type at some point.
        $scopes = 'Describe', 'Context', 'It'
    
        return ([array]::IndexOf($scopes, $CallScope) -ge [array]::IndexOf($scopes, $DesiredScope))
    }
    
    function Exit-MockScope {
        if ($null -eq $mockTable) { return }
    
        $currentScope = $pester.Scope
        $parentScope = $pester.ParentScope
    
        $scriptBlock =
        {
            param ([string] $CommandName)
    
            $ExecutionContext.InvokeProvider.Item.Remove("Function:\$CommandName", $false, $true, $true)
            if ($ExecutionContext.InvokeProvider.Item.Exists("Function:\PesterIsMocking_$CommandName", $true, $true))
            {
                $ExecutionContext.InvokeProvider.Item.Rename("Function:\PesterIsMocking_$CommandName", "script:$CommandName", $true)
            }
        }
    
        $mockKeys = [string[]]$mockTable.Keys
    
        foreach ($mockKey in $mockKeys)
        {
            $mock = $mockTable[$mockKey]
            $mock.Blocks = @($mock.Blocks | Where {$_.Scope -ne $currentScope})
    
            if ($null -eq $parentScope)
            {
                $null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $mock.CommandName
                $mockTable.Remove($mockKey)
            }
            else
            {
                foreach ($historyEntry in $mock.CallHistory)
                {
                    if ($historyEntry.Scope -eq $currentScope) { $historyEntry.Scope = $parentScope }
                }
            }
        }
    }
    
    function Validate-Command([string]$CommandName, [string]$ModuleName) {
        $module = $null
        $origCommand = $null
    
        $scriptBlock = { $ExecutionContext.InvokeCommand.GetCommand($args[0], 'All') }
    
        if ($ModuleName) {
            $module = Microsoft.PowerShell.Core\Get-Module $ModuleName -All |
                      Sort ModuleType |
                      Where { ($origCommand = & $_ $scriptBlock $commandName) } |
                      Select -First 1
        }
    
        $session = $pester.SessionState
    
        if (-not $origCommand) {
            Set-ScriptBlockScope -ScriptBlock $scriptBlock -SessionState $session
            $origCommand = & $scriptBlock $commandName
        }
    
        if ($origCommand -and $origCommand.CommandType -eq [System.Management.Automation.CommandTypes]::Alias) {
            $origCommand = $origCommand.ResolvedCommand
        }
    
        if (-not $origCommand) {
            throw ([System.Management.Automation.CommandNotFoundException] "Could not find Command $commandName")
        }
    
        if ($module) {
            $session = & @($module)[0] { $ExecutionContext.SessionState }
        }
    
        @{Command = $origCommand; Session = $session}
    }
    
    function MockPrototype {
        # It's necessary to strongly type our variable assignments here, just in case the mocked command has
        # parameters of the same names with a different type.  We don't actually care about overwriting the
        # variables, since they're going to be passed along with $PSBoundParameters anyway.
    
        [string] $functionName = $MyInvocation.MyCommand.Name
    
        [string] $moduleName = ''
        if ($ExecutionContext.SessionState.Module)
        {
            $moduleName = $ExecutionContext.SessionState.Module.Name
        }
    
        [object] $ArgumentList = Get-Variable -Name args -ValueOnly -Scope Local -ErrorAction (Get-IgnoreErrorPreference)
        if ($null -eq $ArgumentList) { $ArgumentList = @() }
    
        Invoke-Mock -CommandName $functionName -ModuleName $moduleName -BoundParameters $PSBoundParameters -ArgumentList $ArgumentList
    }
    
    function Invoke-Mock {
        <#
            .SYNOPSIS
            This command is used by Pester's Mocking framework.  You do not need to call it directly.
        #>
    
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [string]
            $CommandName,
    
            [string]
            $ModuleName,
    
            [hashtable]
            $BoundParameters = @{},
    
            [object[]]
            $ArgumentList = @()
        )
    
        if ($mock = $mockTable["$ModuleName||$CommandName"])
        {
            for ($idx = $mock.Blocks.Length; $idx -gt 0; $idx--)
            {
                $block = $mock.Blocks[$idx - 1]
    
                $params = @{
                    ScriptBlock     = $block.Filter
                    BoundParameters = $BoundParameters
                    ArgumentList    = $ArgumentList
                    Metadata        = $mock.Metadata
                }
    
                if (Test-ParameterFilter @params)
                {
                    $block.Verifiable = $false
                    $mock.CallHistory += @{CommandName = "$ModuleName||$CommandName"; BoundParams = $BoundParameters; Args = $ArgumentList; Scope = $pester.Scope }
    
                    $scriptBlock = {
                        param (
                            [Parameter(Mandatory = $true)]
                            [scriptblock]
                            $ScriptBlock,
    
                            [hashtable]
                            $BoundParameters = @{},
    
                            [object[]]
                            $ArgumentList = @(),
    
                            [System.Management.Automation.CommandMetadata]
                            $Metadata
                        )
    
                        # This script block exists to hold variables without polluting the test script's current scope.
                        # Dynamic parameters in functions, for some reason, only exist in $PSBoundParameters instead
                        # of being assigned a local variable the way static parameters do.  By calling Set-DynamicParameterValues,
                        # we create these variables for the caller's use in a Parameter Filter or within the mock itself, and
                        # by doing it inside this temporary script block, those variables don't stick around longer than they
                        # should.
    
                        # Because Set-DynamicParameterVariables might potentially overwrite our $ScriptBlock, $BoundParameters and/or $ArgumentList variables,
                        # we'll stash them in names unlikely to be overwritten.
    
                        $___ScriptBlock___ = $ScriptBlock
                        $___BoundParameters___ = $BoundParameters
                        $___ArgumentList___ = $ArgumentList
    
                        Set-DynamicParameterVariables -SessionState $ExecutionContext.SessionState -Parameters $BoundParameters -Metadata $Metadata
                        & $___ScriptBlock___ @___BoundParameters___ @___ArgumentList___
                    }
    
                    Set-ScriptBlockScope -ScriptBlock $scriptBlock -SessionState $mock.SessionState
                    & $scriptBlock -ScriptBlock $block.Mock -ArgumentList $ArgumentList -BoundParameters $BoundParameters -Metadata $mock.Metadata
    
                    return
                }
            }
    
            & $mock.OriginalCommand @ArgumentList @BoundParameters
        }
        elseif ($mock = $mockTable["||$CommandName"])
        {
            # This situation can happen if the test script is dot-sourced in the global scope.  Under these conditions,
            # a module can wind up executing Invoke-Mock when that was not the intent of the test.  Try to recover from
            # this by executing the original command.
    
            & $mock.OriginalCommand @ArgumentList @BoundParameters
        }
        else
        {
            # If this ever happens, it's a bug in Pester.  The scriptBlock that calls Invoke-Mock should be removed at the same time as the entry in the mock table.
            throw "Internal error detected:  Mock for '$CommandName' in module '$ModuleName' was called, but does not exist in the mock table."
        }
    }
    
    function Invoke-InMockScope
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [System.Management.Automation.SessionState]
            $SessionState,
    
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock,
    
            [Parameter(ValueFromRemainingArguments = $true)]
            [object[]]
            $ArgumentList = @()
        )
    
        if ($SessionState.Module)
        {
            $SessionState.Module.Invoke($ScriptBlock, $ArgumentList)
        }
        else
        {
            Set-ScriptBlockScope -ScriptBlock $ScriptBlock -SessionState $SessionState
            & $ScriptBlock @ArgumentList
        }
    }
    
    function Test-ParameterFilter
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock,
    
            [System.Collections.IDictionary]
            $BoundParameters,
    
            [object[]]
            $ArgumentList,
    
            [System.Management.Automation.CommandMetadata]
            $Metadata
        )
    
        if ($null -eq $BoundParameters)   { $BoundParameters = @{} }
        if ($null -eq $ArgumentList)      { $ArgumentList = @() }
    
        $paramBlock = Get-ParamBlockFromBoundParameters -BoundParameters $BoundParameters -Metadata $Metadata
    
        $scriptBlockString = "
            $paramBlock
    
            Set-StrictMode -Off
            $ScriptBlock
        "
    
        $cmd = [scriptblock]::Create($scriptBlockString)
        Set-ScriptBlockScope -ScriptBlock $cmd -SessionState $pester.SessionState
    
        & $cmd @BoundParameters @ArgumentList
    }
    
    function Get-ParamBlockFromBoundParameters
    {
        param (
            [System.Collections.IDictionary] $BoundParameters,
            [System.Management.Automation.CommandMetadata] $Metadata
        )
    
        $params = foreach ($paramName in $BoundParameters.Keys)
        {
            if (IsCommonParameter -Name $paramName -Metadata $Metadata)
            {
                continue
            }
    
            "`${$paramName}"
        }
    
        $params = $params -join ','
    
        if ($null -ne $Metadata)
        {
            $cmdletBinding = [System.Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($Metadata)
        }
        else
        {
            $cmdletBinding = ''
        }
    
        return "$cmdletBinding param ($params)"
    }
    
    function IsCommonParameter
    {
        param (
            [string] $Name,
            [System.Management.Automation.CommandMetadata] $Metadata
        )
    
        if ($null -ne $Metadata)
        {
            if ([System.Management.Automation.Internal.CommonParameters].GetProperty($Name)) { return $true }
            if ($Metadata.SupportsShouldProcess -and [System.Management.Automation.Internal.ShouldProcessParameters].GetProperty($Name)) { return $true }
            if ($Metadata.SupportsPaging -and [System.Management.Automation.PagingParameters].GetProperty($Name)) { return $true }
            if ($Metadata.SupportsTransactions -and [System.Management.Automation.Internal.TransactionParameters].GetProperty($Name)) { return $true }
        }
    
        return $false
    }
    
    function Get-ScopeForMock
    {
        param ($PesterState)
    
        $scope = $PesterState.Scope
        if ($scope -eq 'It') { $scope = $PesterState.ParentScope }
    
        return $scope
    }
    
    function Set-DynamicParameterVariables
    {
        <#
            .SYNOPSIS
            This command is used by Pester's Mocking framework.  You do not need to call it directly.
        #>
    
        param (
            [Parameter(Mandatory = $true)]
            [System.Management.Automation.SessionState]
            $SessionState,
    
            [hashtable]
            $Parameters,
    
            [System.Management.Automation.CommandMetadata]
            $Metadata
        )
    
        if ($null -eq $Parameters) { $Parameters = @{} }
    
        foreach ($keyValuePair in $Parameters.GetEnumerator())
        {
            $variableName = $keyValuePair.Key
    
            if (-not (IsCommonParameter -Name $variableName -Metadata $Metadata))
            {
                if ($ExecutionContext.SessionState -eq $SessionState)
                {
                    Set-Variable -Scope 1 -Name $variableName -Value $keyValuePair.Value -Force -Confirm:$false -WhatIf:$false
                }
                else
                {
                    $SessionState.PSVariable.Set($variableName, $keyValuePair.Value)
                }
            }
        }
    }
    
    function Get-DynamicParamBlock
    {
        param (
            [scriptblock] $ScriptBlock
        )
    
        if ($PSVersionTable.PSVersion.Major -le 2)
        {
            $flags = [System.Reflection.BindingFlags]'Instance, NonPublic'
            $dynamicParams = [scriptblock].GetField('_dynamicParams', $flags).GetValue($ScriptBlock)
    
            if ($null -ne $dynamicParams)
            {
                return $dynamicParams.ToString()
    
            }
        }
        else
        {
            if ($null -ne $ScriptBlock.Ast.Body.DynamicParamBlock)
            {
                $statements = $ScriptBlock.Ast.Body.DynamicParamBlock.Statements |
                              Select-Object -ExpandProperty Extent |
                              Select-Object -ExpandProperty Text
    
                return $statements -join "`r`n"
            }
        }
    }
    
    function Get-MockDynamicParameters
    {
        <#
            .SYNOPSIS
            This command is used by Pester's Mocking framework.  You do not need to call it directly.
        #>
    
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true, ParameterSetName = 'Cmdlet')]
            [string] $CmdletName,
    
            [Parameter(Mandatory = $true, ParameterSetName = 'Function')]
            [string] $FunctionName,
    
            [Parameter(ParameterSetName = 'Function')]
            [string] $ModuleName,
    
            [hashtable] $Parameters
        )
    
        switch ($PSCmdlet.ParameterSetName)
        {
            'Cmdlet'
            {
                Get-DynamicParametersForCmdlet -CmdletName $CmdletName -Parameters $Parameters
            }
    
            'Function'
            {
                Get-DynamicParametersForMockedFunction -FunctionName $FunctionName -ModuleName $ModuleName -Parameters $Parameters
            }
        }
    }
    
    function Get-DynamicParametersForCmdlet
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [string] $CmdletName,
    
            [System.Collections.IDictionary] $Parameters
        )
    
        if ($null -eq $Parameters) { $Parameters = @{} }
    
        try
        {
            $command = Get-Command -Name $CmdletName -CommandType Cmdlet -ErrorAction Stop
    
            if (@($command).Count -gt 1)
            {
                throw "Name '$CmdletName' resolved to multiple Cmdlets"
            }
        }
        catch
        {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    
        $cmdlet = New-Object $command.ImplementingType.FullName
        if ($cmdlet -isnot [System.Management.Automation.IDynamicParameters])
        {
            return
        }
    
        $flags = [System.Reflection.BindingFlags]'Instance, Nonpublic'
        $context = $ExecutionContext.GetType().GetField('_context', $flags).GetValue($ExecutionContext)
        [System.Management.Automation.Cmdlet].GetProperty('Context', $flags).SetValue($cmdlet, $context, $null)
    
        foreach ($keyValuePair in $Parameters.GetEnumerator())
        {
            $property = $cmdlet.GetType().GetProperty($keyValuePair.Key)
            if ($null -eq $property -or -not $property.CanWrite) { continue }
    
            $isParameter = [bool]($property.GetCustomAttributes([System.Management.Automation.ParameterAttribute], $true))
            if (-not $isParameter) { continue }
    
            $property.SetValue($cmdlet, $keyValuePair.Value, $null)
        }
    
        try 
        {
            $cmdlet.GetDynamicParameters()
        }
        catch [System.NotImplementedException] 
        { 
            #ignore the exception 
        }
    }
    
    function Get-DynamicParametersForMockedFunction
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [string]
            $FunctionName,
    
            [string]
            $ModuleName,
    
            [System.Collections.IDictionary]
            $Parameters
        )
    
        $mock = $mockTable["$ModuleName||$FunctionName"]
    
        if (-not $mock)
        {
            throw "Internal error detected:  Mock for '$FunctionName' in module '$ModuleName' was called, but does not exist in the mock table."
        }
    
        if ($mock.DynamicParamScriptBlock)
        {
            return & $mock.DynamicParamScriptBlock @Parameters
        }
    }
    
  • tools\Functions\New-Fixture.ps1 Show
    function New-Fixture {
        <#
        .SYNOPSIS
        This function generates two scripts, one that defines a function
        and another one that contains its tests.
    
        .DESCRIPTION
        This function generates two scripts, one that defines a function
        and another one that contains its tests. The files are by default
        placed in the current directory and are called and populated as such:
    
    
        The script defining the funciton: .\Clean.ps1:
    
        function Clean {
    
        }
    
        The script containg the example test .\Clean.Tests.ps1:
    
        $here = Split-Path -Parent $MyInvocation.MyCommand.Path
        $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
        . "$here\$sut"
    
        Describe "Clean" {
    
            It "does something useful" {
                $false | Should Be $true
            }
        }
    
    
        .PARAMETER Name
        Defines the name of the function and the name of the test to be created.
    
        .PARAMETER Path
        Defines path where the test and the function should be created, you can use full or relative path.
        If the parameter is not specified the scripts are created in the current directory.
    
        .EXAMPLE
        New-Fixture -Name Clean
    
        Creates the scripts in the current directory.
    
        .EXAMPLE
        New-Fixture C:\Projects\Cleaner Clean
    
        Creates the scripts in the C:\Projects\Cleaner directory.
    
        .EXAMPLE
        New-Fixture Cleaner Clean
    
        Creates a new folder named Cleaner in the current directory and creates the scripts in it.
    
        .LINK
        Describe
        Context
        It
        about_Pester
        about_Should
        #>
    
        param (
            [String]$Path = $PWD,
            [Parameter(Mandatory=$true)]
            [String]$Name
        )
        #region File contents
        #keep this formatted as is. the forma is output to the file as is, including indentation
        $scriptCode = "function $name {`r`n`r`n}"
    
        $testCode = '$here = Split-Path -Parent $MyInvocation.MyCommand.Path
    $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
    . "$here\$sut"
    
    Describe "#name#" {
        It "does something useful" {
            $true | Should Be $false
        }
    }' -replace "#name#",$name
    
        #endregion
    
        $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
    
        Create-File -Path $Path -Name "$Name.ps1" -Content $scriptCode
        Create-File -Path $Path -Name "$Name.Tests.ps1" -Content $testCode
    }
    
    function Create-File ($Path,$Name,$Content) {
        if (-not (Test-Path -Path $Path)) {
            New-Item -ItemType Directory -Path $Path | Out-Null
        }
    
        $FullPath = Join-Path -Path $Path -ChildPath $Name
        if (-not (Test-Path -Path $FullPath)) {
            Set-Content -Path  $FullPath -Value $Content -Encoding UTF8
            Get-Item -Path $FullPath
        }
        else
        {
            Write-Warning "Skipping the file '$FullPath', because it already exists."
        }
    }
    
  • tools\Functions\New-Fixture.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    Describe "New-Fixture" {
        It "Name parameter is mandatory:" {
            (get-command New-Fixture ).Parameters.Name.ParameterSets.__AllParameterSets.IsMandatory | Should Be $true
        }
    
        Context "Only Name parameter is specified:" {
            It "Creates fixture in current directory:" {
                $name = "Test-Fixture"
                $path = "TestDrive:\"
    
                pushd  $path
                New-Fixture -Name $name | Out-Null
                popd
    
                Join-Path -Path $path -ChildPath "$name.ps1" | Should Exist
                Join-Path -Path $path -ChildPath "$name.Tests.ps1" | Should Exist
            }
        }
    
        Context "Name and Path parameter is specified:" {
            #use different fixture names to avoid interference among the test cases
            #claning up would be also possible, but difficult if the assertion fails
            It "Creates fixture in full Path:" {
                $name = "Test-Fixture"
                $path = "TestDrive:\full"
    
                New-Fixture -Name $name -Path $path | Out-Null
    
                Join-Path -Path $path -ChildPath "$name.ps1" | Should Exist
                Join-Path -Path $path -ChildPath "$name.Tests.ps1" | Should Exist
    
                #cleanup
                Join-Path -Path "$path" -ChildPath "$name.ps1" | Remove-Item -Force
                Join-Path -Path "$path" -ChildPath "$name.Tests.ps1" | Remove-Item -Force
            }
    
            It "Creates fixture in relative Path:" {
                $name = "Relative1-Fixture"
                $path = "TestDrive:\"
    
                pushd  $path
                New-Fixture -Name $name -Path relative | Out-Null
                popd
    
                Join-Path -Path "$path\relative" -ChildPath "$name.ps1" | Should Exist
                Join-Path -Path "$path\relative" -ChildPath "$name.Tests.ps1" | Should Exist
            }
            It "Creates fixture if Path is set to '.':" {
                $name = "Relative2-Fixture"
                $path = "TestDrive:\"
    
                pushd  $path
                New-Fixture -Name $name -Path . | Out-Null
                popd
    
                Join-Path -Path "$path" -ChildPath "$name.ps1" | Should Exist
                Join-Path -Path "$path" -ChildPath "$name.Tests.ps1" | Should Exist
            }
            It "Creates fixture if Path is set to '(pwd)':" {
                $name = "Relative3-Fixture"
                $path = "TestDrive:\"
    
                pushd  $path
                New-Fixture -Name $name -Path (pwd) | Out-Null
                popd
    
                Join-Path -Path "$path" -ChildPath "$name.ps1" | Should Exist
                Join-Path -Path "$path" -ChildPath "$name.Tests.ps1" | Should Exist
            }
            It "Writes warning if file exists" {
                $name = "Warning-Fixture"
                $path = "TestDrive:\"
    
                Mock -Verifiable -ModuleName Pester Write-Warning { }
    
                #Create the same files twice
                New-Fixture -Name $name -Path $path | Out-Null
                New-Fixture -Name $name -Path $path -WarningVariable warnings -WarningAction SilentlyContinue | Out-Null
    
                Assert-VerifiableMocks
            }
    
        }
        #TODO add tests that validate the contents of default files
    }
    
    
  • tools\Functions\TestDrive.ps1 Show
    #
    function New-TestDrive ([Switch]$PassThru) {
        $Path = New-RandomTempDirectory
        $DriveName = "TestDrive"
    
        if (-not (Microsoft.PowerShell.Management\Test-Path -Path $Path))
        {
            New-Item -ItemType Container -Path $Path | Out-Null
        }
    
        #setup the test drive
        if ( -not (Test-Path "${DriveName}:\") )
        {
            New-PSDrive -Name $DriveName -PSProvider FileSystem -Root $Path -Scope Global -Description "Pester test drive" | Out-Null
        }
    
        #publish the global TestDrive variable used in few places within the module
        if (-not (Test-Path "Variable:Global:DriveName"))
        {
            New-Variable -Name $DriveName -Scope Global -Value $Path
        }
    
        if ( $PassThru ) { Get-PSDrive -Name $DriveName }
    }
    
    
    function Clear-TestDrive ([String[]]$Exclude) {
        $Path = (Microsoft.PowerShell.Management\Get-PSDrive -Name TestDrive).Root
        if (Microsoft.PowerShell.Management\Test-Path -Path $Path )
        {
            #Get-ChildItem -Exclude did not seem to work with full paths
            Microsoft.PowerShell.Management\Get-ChildItem -Recurse -Path $Path |
            Microsoft.PowerShell.Utility\Sort-Object -Descending  -Property "FullName" |
            Microsoft.PowerShell.Core\Where-Object { $Exclude -NotContains $_.FullName } |
            Microsoft.PowerShell.Management\Remove-Item -Force -Recurse
        }
    }
    
    function New-RandomTempDirectory {
        do
        {
            $Path = Join-Path -Path $env:TEMP -ChildPath ([Guid]::NewGuid())
        } until (-not (  Microsoft.PowerShell.Management\Test-Path -Path $Path ))
    
        New-Item -ItemType Container -Path $Path
    }
    
    function Get-TestDriveItem {
        #moved here from Pester.psm1
        param( [string]$Path )
    
        Assert-DescribeInProgress -CommandName Get-TestDriveItem
        Get-Item $(Join-Path $TestDrive $Path )
    }
    
    function Get-TestDriveChildItem {
        $Path = (Microsoft.PowerShell.Management\Get-PSDrive -Name TestDrive).Root
        if (Microsoft.PowerShell.Management\Test-Path -Path $Path )
        {
            Microsoft.PowerShell.Management\Get-ChildItem -Recurse -Path $Path
        }
    }
    
    function Remove-TestDrive {
    
        $DriveName = "TestDrive"
        $Drive = Get-PSDrive -Name $DriveName -ErrorAction (Get-IgnoreErrorPreference)
        $Path = ($Drive).Root
    
    
        if ($pwd -like "$DriveName*" ) {
            #will staying in the test drive cause issues?
            #TODO review this
            Write-Warning -Message "Your current path is set to ${pwd}:. You should leave ${DriveName}:\ before leaving Describe."
        }
    
        if ( $Drive )
        {
            $Drive | Remove-PSDrive -Force -ErrorAction (Get-IgnoreErrorPreference)
        }
    
        if (Microsoft.PowerShell.Management\Test-Path -Path $Path)
        {
            Microsoft.PowerShell.Management\Remove-Item -Path $Path -Force -Recurse
        }
    
        if (Get-Variable -Name $DriveName -Scope Global -ErrorAction (Get-IgnoreErrorPreference)) {
            Remove-Variable -Scope Global -Name $DriveName -Force
        }
    }
    
    function Setup {
        #included for backwards compatibility
        param(
        [switch]$Dir,
        [switch]$File,
        $Path,
        $Content = "",
        [switch]$PassThru
        )
    
        Assert-DescribeInProgress -CommandName Setup
    
        $TestDriveName = Get-PSDrive TestDrive | Select -ExpandProperty Root
    
        if ($Dir) {
            $item = New-Item -Name $Path -Path "${TestDriveName}\" -Type Container -Force
        }
        if ($File) {
            $item = $Content | New-Item -Name $Path -Path "${TestDriveName}\" -Type File -Force
        }
    
        if($PassThru) {
            return $item
        }
    }
    
  • tools\Functions\TestDrive.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    Describe "Setup" {
        It "returns a location that is in a temp area" {
            $TestDrive -like "${$env:temp}*" | Should Be $true
        }
    
        It "creates a drive location called TestDrive:" {
            "TestDrive:\" | Should Exist
        }
    }
    
    Describe "TestDrive" {
        It "handles creation of a drive with . characters in the path" {
            #TODO: currently untested but requirement needs to be here
            "preventing this from failing"
        }
    }
    
    Describe "Create filesystem with directories" {
        Setup -Dir "dir1"
        Setup -Dir "dir2"
    
        It "creates directory when called with no file content" {
            "TestDrive:\dir1" | Should Exist
        }
    
        It "creates another directory when called with no file content and doesnt remove first directory" {
            $result = Test-Path "TestDrive:\dir2"
            $result = $result -and (Test-Path "TestDrive:\dir1")
            $result | Should Be $true
        }
    }
    
    Describe "Create nested directory structure" {
        Setup -Dir "parent/child"
    
        It "creates parent directory" {
            "TestDrive:\parent" | Should Exist
        }
    
        It "creates child directory underneath parent" {
            "TestDrive:\parent\child" | Should Exist
        }
    }
    
    Describe "Create a file with no content" {
        Setup -File "file"
    
        It "creates file" {
            "TestDrive:\file" | Should Exist
        }
    
        It "also has no content" {
            Get-Content "TestDrive:\file" | Should BeNullOrEmpty
        }
    }
    
    Describe "Create a file with content" {
        Setup -File "file" "file contents"
    
        It "creates file" {
            "TestDrive:\file" | Should Exist
        }
    
        It "adds content to the file" {
            Get-Content "TestDrive:\file" | Should Be "file contents"
        }
    }
    
    Describe "Create file with passthru" {
        $thefile = Setup -File "thefile" -PassThru
    
        It "returns the file from the temp location" {
            $thefile.FullName -like "${env:TEMP}*" | Should Be $true
            $thefile.Exists | Should Be $true
        }
    }
    
    Describe "Create directory with passthru" {
        $thedir = Setup -Dir "thedir" -PassThru
    
        It "returns the directory from the temp location" {
            $thedir.FullName -like "${env:TEMP}*" | Should Be $true
            $thedir.Exists | Should Be $true
        }
    }
    
    Describe "TestDrive scoping" {
        $describe = Setup -File 'Describe' -PassThru
        Context "Describe file is available in context" {
            It "Finds the file" {
                $describe | Should Exist
            }
            #create file for the next test
            Setup -File 'Context'
    
            It "Creates It-scoped contents" {
                Setup -File 'It'
                'TestDrive:\It' | Should Exist
            }
    
            It "Does not clear It-scoped contents on exit" {
                'TestDrive:\It' | Should Exist
            }
        }
    
        It "Context file are removed when returning to Describe" {
            "TestDrive:\Context" | Should Not Exist
        }
    
        It "Describe file is still available in Describe" {
            $describe | Should Exist
        }
    }
    
    Describe "Cleanup" {
        Setup -Dir "foo"
    }
    
    Describe "Cleanup" {
        It "should have removed the temp folder from the previous fixture" {
            Test-Path "$TestDrive\foo" | Should Not Exist
        }
    
        It "should also remove the TestDrive:" {
            Test-Path "TestDrive:\foo" | Should Not Exist
        }
    }
    
    Describe "Cleanup when Remove-Item is mocked" {
        Mock Remove-Item {}
    
        Context "add a temp directory" {
            Setup -Dir "foo"
        }
    
        Context "next context" {
    
            It "should have removed the temp folder" {
                "$TestDrive\foo" | Should Not Exist
            }
    
        }
    }
    
    InModuleScope Pester {
        Describe "New-RandomTempDirectory" {
            It "creates randomly named directory" {
                $first = New-RandomTempDirectory
                $second = New-RandomTempDirectory
    
                $first | Remove-Item -Force
                $second | Remove-Item -Force
    
                $first.name | Should Not Be $second.name
    
            }
        }
    }
    
  • tools\Functions\TestResults.ps1 Show
    function Get-HumanTime($Seconds) {
        if($Seconds -gt 0.99) {
            $time = [math]::Round($Seconds, 2)
            $unit = 's'
        }
        else {
            $time = [math]::Floor($Seconds * 1000)
            $unit = 'ms'
        }
        return "$time$unit"
    }
    
    function GetFullPath ([string]$Path) {
        if (-not [System.IO.Path]::IsPathRooted($Path))
        {
            $Path = Join-Path $ExecutionContext.SessionState.Path.CurrentFileSystemLocation $Path
        }
    
        return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
    }
    
    function Export-PesterResults
    {
        param (
            $PesterState,
            [string] $Path,
            [string] $Format
        )
    
        switch ($Format)
        {
            'LegacyNUnitXml' { Export-NUnitReport -PesterState $PesterState -Path $Path -LegacyFormat }
            'NUnitXml'       { Export-NUnitReport -PesterState $PesterState -Path $Path }
    
            default
            {
                throw "'$Format' is not a valid Pester export format."
            }
        }
    }
    function Export-NUnitReport {
        param (
            [parameter(Mandatory=$true,ValueFromPipeline=$true)]
            $PesterState,
    
            [parameter(Mandatory=$true)]
            [String]$Path,
    
            [switch] $LegacyFormat
        )
    
        #the xmlwriter create method can resolve relatives paths by itself. but its current directory might
        #be different from what PowerShell sees as the current directory so I have to resolve the path beforehand
        #working around the limitations of Resolve-Path
    
        $Path = GetFullPath -Path $Path
    
        $settings = New-Object -TypeName Xml.XmlWriterSettings -Property @{
            Indent = $true
            NewLineOnAttributes = $false
        }
    
        $xmlWriter = $null
        try {
            $xmlWriter = [Xml.XmlWriter]::Create($Path,$settings)
    
            Write-NUnitReport -XmlWriter $xmlWriter -PesterState $PesterState -LegacyFormat:$LegacyFormat
    
            $xmlWriter.Flush()
        }
        finally
        {
            if ($null -ne $xmlWriter) {
                try { $xmlWriter.Close() } catch {}
            }
        }
    }
    
    function Write-NUnitReport($PesterState, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat)
    {
        # Write the XML Declaration
        $XmlWriter.WriteStartDocument($false)
    
        # Write Root Element
        $xmlWriter.WriteStartElement('test-results')
    
        Write-NUnitTestResultAttributes @PSBoundParameters
        Write-NUnitTestResultChildNodes @PSBoundParameters
    
        $XmlWriter.WriteEndElement()
    }
    
    function Write-NUnitTestResultAttributes($PesterState, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat)
    {
        $XmlWriter.WriteAttributeString('xmlns','xsi', $null, 'http://www.w3.org/2001/XMLSchema-instance')
        $XmlWriter.WriteAttributeString('xsi','noNamespaceSchemaLocation', [Xml.Schema.XmlSchema]::InstanceNamespace , 'nunit_schema_2.5.xsd')
        $XmlWriter.WriteAttributeString('name','Pester')
        $XmlWriter.WriteAttributeString('total', $PesterState.TotalCount)
        $XmlWriter.WriteAttributeString('errors', '0')
        $XmlWriter.WriteAttributeString('failures', $PesterState.FailedCount)
        $XmlWriter.WriteAttributeString('not-run', '0')
        $XmlWriter.WriteAttributeString('inconclusive', $PesterState.PendingCount)
        $XmlWriter.WriteAttributeString('ignored', '0')
        $XmlWriter.WriteAttributeString('skipped', $PesterState.SkippedCount)
        $XmlWriter.WriteAttributeString('invalid', '0')
        $date = Get-Date
        $XmlWriter.WriteAttributeString('date', (Get-Date -Date $date -Format 'yyyy-MM-dd'))
        $XmlWriter.WriteAttributeString('time', (Get-Date -Date $date -Format 'HH:mm:ss'))
    }
    
    function Write-NUnitTestResultChildNodes($PesterState, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat)
    {
        Write-NUnitEnvironmentInformation @PSBoundParameters
        Write-NUnitCultureInformation @PSBoundParameters
    
        $XmlWriter.WriteStartElement('test-suite')
        Write-NUnitGlobalTestSuiteAttributes @PSBoundParameters
    
        $XmlWriter.WriteStartElement('results')
    
        Write-NUnitDescribeElements @PSBoundParameters
    
        $XmlWriter.WriteEndElement()
        $XmlWriter.WriteEndElement()
    }
    
    function Write-NUnitEnvironmentInformation($PesterState, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat)
    {
        $XmlWriter.WriteStartElement('environment')
    
        $environment = Get-RunTimeEnvironment
        foreach ($keyValuePair in $environment.GetEnumerator()) {
            $XmlWriter.WriteAttributeString($keyValuePair.Name, $keyValuePair.Value)
        }
    
        $XmlWriter.WriteEndElement()
    }
    
    function Write-NUnitCultureInformation($PesterState, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat)
    {
        $XmlWriter.WriteStartElement('culture-info')
    
        $XmlWriter.WriteAttributeString('current-culture', ([System.Threading.Thread]::CurrentThread.CurrentCulture).Name)
        $XmlWriter.WriteAttributeString('current-uiculture', ([System.Threading.Thread]::CurrentThread.CurrentUiCulture).Name)
    
        $XmlWriter.WriteEndElement()
    }
    
    function Write-NUnitGlobalTestSuiteAttributes($PesterState, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat)
    {
        $XmlWriter.WriteAttributeString('type', 'Powershell')
        $XmlWriter.WriteAttributeString('name', $PesterState.Path)
        $XmlWriter.WriteAttributeString('executed', 'True')
    
        $isSuccess = $PesterState.FailedCount -eq 0
        $result = Get-ParentResult $PesterState
        $XmlWriter.WriteAttributeString('result', $result)
        $XmlWriter.WriteAttributeString('success',[string]$isSuccess)
        $XmlWriter.WriteAttributeString('time',(Convert-TimeSpan $PesterState.Time))
        $XmlWriter.WriteAttributeString('asserts','0')
    }
    
    function Write-NUnitDescribeElements($PesterState, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat)
    {
        $Describes = $PesterState.TestResult | Group-Object -Property Describe
        foreach ($currentDescribe in $Describes)
        {
            $DescribeInfo = Get-TestSuiteInfo $currentDescribe
    
            #Write test suites
            $XmlWriter.WriteStartElement('test-suite')
    
            if ($LegacyFormat) { $suiteType = 'PowerShell' } else { $suiteType = 'TestFixture' }
    
            Write-NUnitTestSuiteAttributes -TestSuiteInfo $DescribeInfo -TestSuiteType $suiteType -XmlWriter $XmlWriter -LegacyFormat:$LegacyFormat
    
            $XmlWriter.WriteStartElement('results')
    
            Write-NUnitDescribeChildElements -TestResults $currentDescribe.Group -XmlWriter $XmlWriter -LegacyFormat:$LegacyFormat -DescribeName $DescribeInfo.Name
    
            $XmlWriter.WriteEndElement()
            $XmlWriter.WriteEndElement()
        }
    }
    
    function Get-TestSuiteInfo ([Microsoft.PowerShell.Commands.GroupInfo]$TestSuiteGroup)
    {
        $suite = @{
            resultMessage = 'Failure'
            success = 'False'
            totalTime = '0.0'
            name = $TestSuiteGroup.Name
            description = $TestSuiteGroup.Name
        }
    
        #calculate the time first, I am converting the time into string in the TestCases
        $suite.totalTime = (Get-TestTime $TestSuiteGroup.Group)
        $suite.success = (Get-TestSuccess $TestSuiteGroup.Group)
        $suite.resultMessage = Get-GroupResult $TestSuiteGroup.Group
        $suite
    }
    
    function Get-TestTime($tests) {
        [TimeSpan]$totalTime = 0;
        if ($tests)
        {
            foreach ($test in $tests)
            {
                $totalTime += $test.time
            }
        }
    
        Convert-TimeSpan -TimeSpan $totalTime
    }
    function Convert-TimeSpan {
        param (
            [Parameter(ValueFromPipeline=$true)]
            $TimeSpan
        )
        process {
            if ($TimeSpan) {
                [string][math]::round(([TimeSpan]$TimeSpan).totalseconds,4)
            }
            else
            {
                '0'
            }
        }
    }
    function Get-TestSuccess($tests) {
        $result = $true
        if ($tests)
        {
            foreach ($test in $tests) {
                if (-not $test.Passed) {
                    $result = $false
                    break
                }
            }
        }
        [String]$result
    }
    function Write-NUnitTestSuiteAttributes($TestSuiteInfo, [System.Xml.XmlWriter] $XmlWriter, [string] $TestSuiteType, [switch] $LegacyFormat)
    {
        $XmlWriter.WriteAttributeString('type', $TestSuiteType)
        $XmlWriter.WriteAttributeString('name', $TestSuiteInfo.name)
        $XmlWriter.WriteAttributeString('executed', 'True')
        $XmlWriter.WriteAttributeString('result', $TestSuiteInfo.resultMessage)
        $XmlWriter.WriteAttributeString('success', $TestSuiteInfo.success)
        $XmlWriter.WriteAttributeString('time',$TestSuiteInfo.totalTime)
        $XmlWriter.WriteAttributeString('asserts','0')
    
        if (-not $LegacyFormat)
        {
            $XmlWriter.WriteAttributeString('description', $TestSuiteInfo.Description)
        }
    }
    
    function Write-NUnitDescribeChildElements([object[]] $TestResults, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat, [string] $DescribeName)
    {
        $suites = $TestResults | Group-Object -Property ParameterizedSuiteName
    
        foreach ($suite in $suites)
        {
            if ($suite.Name)
            {
                $suiteInfo = Get-TestSuiteInfo -TestSuiteGroup $suite
    
                $XmlWriter.WriteStartElement('test-suite')
    
                if (-not $LegacyFormat)
                {
                    $suiteInfo.Name = "$DescribeName.$($suiteInfo.Name)"
                }
    
                Write-NUnitTestSuiteAttributes -TestSuiteInfo $suiteInfo -TestSuiteType 'ParameterizedTest' -XmlWriter $XmlWriter -LegacyFormat:$LegacyFormat
    
                $XmlWriter.WriteStartElement('results')
            }
    
            Write-NUnitTestCaseElements -TestResults $suite.Group -XmlWriter $XmlWriter -LegacyFormat:$LegacyFormat -DescribeName $DescribeName -ParameterizedSuiteName $suite.Name
    
            if ($suite.Name)
            {
                $XmlWriter.WriteEndElement()
                $XmlWriter.WriteEndElement()
            }
        }
    }
    
    function Write-NUnitTestCaseElements([object[]] $TestResults, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat, [string] $DescribeName, [string] $ParameterizedSuiteName)
    {
        foreach ($testResult in $TestResults)
        {
            $XmlWriter.WriteStartElement('test-case')
    
            Write-NUnitTestCaseAttributes -TestResult $testResult -XmlWriter $XmlWriter -LegacyFormat:$LegacyFormat -DescribeName $DescribeName -ParameterizedSuiteName $ParameterizedSuiteName
    
            $XmlWriter.WriteEndElement()
        }
    }
    
    function Write-NUnitTestCaseAttributes($TestResult, [System.Xml.XmlWriter] $XmlWriter, [switch] $LegacyFormat, [string] $DescribeName, [string] $ParameterizedSuiteName)
    {
        $testName = $TestResult.Name
    
        if (-not $LegacyFormat)
        {
            if ($testName -eq $ParameterizedSuiteName)
            {
                $paramString = ''
                if ($null -ne $TestResult.Parameters)
                {
                    $params = @(
                        foreach ($value in $TestResult.Parameters.Values)
                        {
                            if ($null -eq $value)
                            {
                                'null'
                            }
                            elseif ($value -is [string])
                            {
                                '"{0}"' -f $value
                            }
                            else
                            {
                                #do not use .ToString() it uses the current culture settings 
                                #and we need to use en-US culture, which [string] or .ToString([Globalization.CultureInfo]'en-us') uses
                                [string]$value
                            }
                        }
                    )
    
                    $paramString = $params -join ','
                }
    
                $testName = "$testName($paramString)"
            }
    
            $testName = "$DescribeName.$testName"
    
            $XmlWriter.WriteAttributeString('description', $TestResult.Name)
        }
    
        $XmlWriter.WriteAttributeString('name', $testName)
        $XmlWriter.WriteAttributeString('executed', 'True')
        $XmlWriter.WriteAttributeString('time', (Convert-TimeSpan $TestResult.Time))
        $XmlWriter.WriteAttributeString('asserts', '0')
        $XmlWriter.WriteAttributeString('success', $TestResult.Passed)
    
        switch ($TestResult.Result)
        {
            Passed
            {
                $XmlWriter.WriteAttributeString('result', 'Success')
                break
            }
            Skipped
            {
                $XmlWriter.WriteAttributeString('result', 'Skipped')
                break
            }
            Pending
            {
                $XmlWriter.WriteAttributeString('result', 'Inconclusive')
                break
            }
            Failed
            {
                $XmlWriter.WriteAttributeString('result', 'Failure')
                $XmlWriter.WriteStartElement('failure')
                $xmlWriter.WriteElementString('message', $TestResult.FailureMessage)
                $XmlWriter.WriteElementString('stack-trace', $TestResult.StackTrace)
                $XmlWriter.WriteEndElement() # Close failure tag
                break
            }
        }
    }
    function Get-RunTimeEnvironment() {
        $osSystemInformation = (Get-WmiObject Win32_OperatingSystem)
        @{
            'nunit-version' = '2.5.8.0'
            'os-version' = $osSystemInformation.Version
            platform = $osSystemInformation.Name
            cwd = (Get-Location).Path #run path
            'machine-name' = $env:ComputerName
            user = $env:Username
            'user-domain' = $env:userDomain
            'clr-version' = [string]$PSVersionTable.ClrVersion
        }
    }
    
    function Exit-WithCode ($FailedCount) {
        $host.SetShouldExit($FailedCount)
    }
    
    function Get-ParentResult ($InputObject)
    {
        #I am not sure about the result precedence, and can't find any good source
        #TODO: Confirm this is the correct order of precedence
        if ($inputObject.FailedCount  -gt 0) { return 'Failure' }
        if ($InputObject.SkippedCount -gt 0) { return 'Skipped' }
        if ($InputObject.PendingCount -gt 0) { return 'Inconclusive' }
        return 'Success'
    }
    
    function Get-GroupResult ($InputObject)
    {
        #I am not sure about the result precedence, and can't find any good source
        #TODO: Confirm this is the correct order of precedence
        if ($InputObject |  Where {$_.Result -eq 'Failed'}) { return 'Failure' }
        if ($InputObject |  Where {$_.Result -eq 'Skipped'}) { return 'Skipped' }
        if ($InputObject |  Where {$_.Result -eq 'Pending'}) { return 'Inconclusive' }
        return 'Success'
    }
    
  • tools\Functions\TestResults.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    InModuleScope Pester {
        Describe "Write nunit test results (Legacy)" {
            Setup -Dir "Results"
    
            It "should write a successful test result" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $TestResults.AddTestResult("Successful testcase","Passed",(New-TimeSpan -Seconds 1))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xmlResult = [xml] (Get-Content $testFile)
                $xmlTestCase = $xmlResult.'test-results'.'test-suite'.'results'.'test-suite'.'results'.'test-case'
                $xmlTestCase.name     | Should Be "Successful testcase"
                $xmlTestCase.result   | Should Be "Success"
                $xmlTestCase.time     | Should Be "1"
            }
    
            It "should write a failed test result" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $time = [TimeSpan]::FromSeconds(2.5)
                $TestResults.AddTestResult("Failed testcase","Failed",$time,'Assert failed: "Expected: Test. But was: Testing"','at line: 28 in  C:\Pester\Result.Tests.ps1')
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xmlResult = [xml] (Get-Content $testFile)
                $xmlTestCase = $xmlResult.'test-results'.'test-suite'.'results'.'test-suite'.'results'.'test-case'
                $xmlTestCase.name                   | Should Be "Failed testcase"
                $xmlTestCase.result                 | Should Be "Failure"
                $xmlTestCase.time                   | Should Be "2.5"
                $xmlTestCase.failure.message        | Should Be 'Assert failed: "Expected: Test. But was: Testing"'
                $xmlTestCase.failure.'stack-trace'  | Should Be 'at line: 28 in  C:\Pester\Result.Tests.ps1'
            }
    
             It "should write the test summary" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $TestResults.AddTestResult("Testcase","Passed",(New-TimeSpan -Seconds 1))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xmlResult = [xml] (Get-Content $testFile)
                $xmlTestResult = $xmlResult.'test-results'
                $xmlTestResult.total    | Should Be 1
                $xmlTestResult.failures | Should Be 0
                $xmlTestResult.date     | Should Be $true
                $xmlTestResult.time     | Should Be $true
            }
    
            it "should write the test-suite information" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $TestResults.AddTestResult("Successful testcase","Passed",[timespan]10000000) #1.0 seconds
                $TestResults.AddTestResult("Successful testcase","Passed",[timespan]11000000) #1.1 seconds
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xmlResult = [xml] (Get-Content $testFile)
    
                $xmlTestResult = $xmlResult.'test-results'.'test-suite'.results.'test-suite'
                $xmlTestResult.type        | Should Be "Powershell"
                $xmlTestResult.name        | Should Be "Mocked Describe"
                $xmlTestResult.description | Should BeNullOrEmpty
                $xmlTestResult.result      | Should Be "Success"
                $xmlTestResult.success     | Should Be "True"
                $xmlTestResult.time        | Should Be 2.1
            }
    
            it "should write two test-suite elements for two describes" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Describe #1')
                $TestResults.AddTestResult("Successful testcase","Passed",(New-TimeSpan -Seconds 1))
                $TestResults.LeaveDescribe()
                $testResults.EnterDescribe('Describe #2')
                $TestResults.AddTestResult("Failed testcase","Failed",(New-TimeSpan -Seconds 2))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xmlResult = [xml] (Get-Content $testFile)
    
                $xmlTestSuite1 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[0]
                $xmlTestSuite1.name        | Should Be "Describe #1"
                $xmlTestSuite1.description | Should BeNullOrEmpty
                $xmlTestSuite1.result      | Should Be "Success"
                $xmlTestSuite1.success     | Should Be "True"
                $xmlTestSuite1.time        | Should Be 1.0
    
                $xmlTestSuite2 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[1]
                $xmlTestSuite2.name        | Should Be "Describe #2"
                $xmlTestSuite2.description | Should BeNullOrEmpty
                $xmlTestSuite2.result      | Should Be "Failure"
                $xmlTestSuite2.success     | Should Be "False"
                $xmlTestSuite2.time        | Should Be 2.0
            }
    
            it "should write parent results in tree correctly" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Failed')
                $TestResults.AddTestResult("Failed","Failed")
                $TestResults.AddTestResult("Skipped","Skipped")
                $TestResults.AddTestResult("Pending","Pending")
                $TestResults.AddTestResult("Passed","Passed")
                $TestResults.LeaveDescribe()
    
                $testResults.EnterDescribe('Skipped')
                $TestResults.AddTestResult("Skipped","Skipped")
                $TestResults.AddTestResult("Pending","Pending")
                $TestResults.AddTestResult("Passed","Passed")
                $TestResults.LeaveDescribe()
    
                $testResults.EnterDescribe('Pending')
                $TestResults.AddTestResult("Pending","Pending")
                $TestResults.AddTestResult("Passed","Passed")
                $TestResults.LeaveDescribe()
    
                $testResults.EnterDescribe('Passed')
                $TestResults.AddTestResult("Passed","Passed")
                $TestResults.LeaveDescribe()
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xmlResult = [xml] (Get-Content $testFile)
    
                $xmlTestSuite1 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[0]
                $xmlTestSuite1.name     | Should Be "Failed"
                $xmlTestSuite1.result   | Should Be "Failure"
                $xmlTestSuite1.success  | Should Be "False"
    
                $xmlTestSuite2 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[1]
                $xmlTestSuite2.name     | Should Be "Skipped"
                $xmlTestSuite2.result   | Should Be "Skipped"
                $xmlTestSuite2.success  | Should Be "True"
    
                $xmlTestSuite3 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[2]
                $xmlTestSuite3.name     | Should Be "Pending"
                $xmlTestSuite3.result   | Should Be "Inconclusive"
                $xmlTestSuite3.success  | Should Be "True"
    
                $xmlTestSuite4 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[3]
                $xmlTestSuite4.name     | Should Be "Passed"
                $xmlTestSuite4.result   | Should Be "Success"
                $xmlTestSuite4.success  | Should Be "True"
    
            }
    
            it "should write the environment information" {
                $state = New-PesterState "."
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $state $testFile -LegacyFormat
                $xmlResult = [xml] (Get-Content $testFile)
    
                $xmlEnvironment = $xmlResult.'test-results'.'environment'
                $xmlEnvironment.'os-Version'    | Should Be $true
                $xmlEnvironment.platform        | Should Be $true
                $xmlEnvironment.cwd             | Should Be (Get-Location).Path
                if ($env:Username) {
                    $xmlEnvironment.user        | Should Be $env:Username
                }
                $xmlEnvironment.'machine-name'  | Should Be $env:ComputerName
            }
    
            it "Should validate test results against the nunit 2.5 schema" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Describe #1')
                $TestResults.AddTestResult("Successful testcase","Passed",(New-TimeSpan -Seconds 1))
                $TestResults.LeaveDescribe()
                $testResults.EnterDescribe('Describe #2')
                $TestResults.AddTestResult("Failed testcase","Failed",(New-TimeSpan -Seconds 2))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xml = [xml] (Get-Content $testFile)
    
                $schemePath = (Get-Module -Name Pester).Path | Split-Path | Join-Path -ChildPath "nunit_schema_2.5.xsd"
                $xml.Schemas.Add($null,$schemePath) > $null
                { $xml.Validate({throw $args.Exception }) } | Should Not Throw
            }
    
            it "handles special characters in block descriptions well [email protected]#$%^&*()_+`1234567890[];'',./""- " {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Describe [email protected]#$%^&*()_+`1234567890[];'',./"- #1')
                $TestResults.AddTestResult("Successful testcase [email protected]#$%^&*()_+`1234567890[];'',./""-","Passed",(New-TimeSpan -Seconds 1))
                $TestResults.LeaveDescribe()
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xml = [xml] (Get-Content $testFile)
    
                $schemePath = (Get-Module -Name Pester).Path | Split-Path | Join-Path -ChildPath "nunit_schema_2.5.xsd"
                $xml.Schemas.Add($null,$schemePath) > $null
                { $xml.Validate({throw $args.Exception }) } | Should Not Throw
            }
    
            Context 'Exporting Parameterized Tests (New Legacy)' {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
    
                $TestResults.AddTestResult(
                    'Parameterized Testcase One',
                    'Passed',
                    (New-TimeSpan -Seconds 1),
                    $null,
                    $null,
                    'Parameterized Testcase <A>',
                    @{ Parameter = 'One' }
                )
    
                $TestResults.AddTestResult(
                    'Parameterized Testcase <A>',
                    'Failed',
                    (New-TimeSpan -Seconds 1),
                    'Assert failed: "Expected: Test. But was: Testing"',
                    'at line: 28 in  C:\Pester\Result.Tests.ps1',
                    'Parameterized Testcase <A>',
                    @{ Parameter = 'Two' }
    
                )
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile -LegacyFormat
                $xmlResult    = [xml] (Get-Content $testFile)
    
                It 'should write parameterized test results correctly' {
                    $xmlTestSuite = $xmlResult.'test-results'.'test-suite'.'results'.'test-suite'.'results'.'test-suite'
    
                    $xmlTestSuite.name        | Should Be 'Parameterized Testcase <A>'
                    $xmlTestSuite.description | Should BeNullOrEmpty
                    $xmlTestSuite.type        | Should Be 'ParameterizedTest'
                    $xmlTestSuite.result      | Should Be 'Failure'
                    $xmlTestSuite.success     | Should Be 'False'
                    $xmlTestSuite.time        | Should Be '2'
    
                    foreach ($testCase in $xmlTestSuite.results.'test-case')
                    {
                        $testCase.Name | Should Match '^Parameterized Testcase (One|<A>)$'
                        $testCase.time | Should Be 1
                    }
                }
    
                it 'Should validate test results against the nunit 2.5 schema' {
                    $schemaPath = (Get-Module -Name Pester).Path | Split-Path | Join-Path -ChildPath "nunit_schema_2.5.xsd"
                    $null = $xmlResult.Schemas.Add($null,$schemaPath)
                    { $xmlResult.Validate({throw $args.Exception }) } | Should Not Throw
                }
            }
        }
    
        Describe "Write nunit test results (Newer format)" {
            Setup -Dir "Results"
    
            It "should write a successful test result" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $TestResults.AddTestResult("Successful testcase",'Passed',(New-TimeSpan -Seconds 1))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xmlResult = [xml] (Get-Content $testFile)
                $xmlTestCase = $xmlResult.'test-results'.'test-suite'.'results'.'test-suite'.'results'.'test-case'
                $xmlTestCase.name     | Should Be "Mocked Describe.Successful testcase"
                $xmlTestCase.result   | Should Be "Success"
                $xmlTestCase.time     | Should Be "1"
            }
    
            It "should write a failed test result" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $time = [TimeSpan]25000000 #2.5 seconds
                $TestResults.AddTestResult("Failed testcase",'Failed',$time,'Assert failed: "Expected: Test. But was: Testing"','at line: 28 in  C:\Pester\Result.Tests.ps1')
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xmlResult = [xml] (Get-Content $testFile)
                $xmlTestCase = $xmlResult.'test-results'.'test-suite'.'results'.'test-suite'.'results'.'test-case'
                $xmlTestCase.name                   | Should Be "Mocked Describe.Failed testcase"
                $xmlTestCase.result                 | Should Be "Failure"
                $xmlTestCase.time                   | Should Be "2.5"
                $xmlTestCase.failure.message        | Should Be 'Assert failed: "Expected: Test. But was: Testing"'
                $xmlTestCase.failure.'stack-trace'  | Should Be 'at line: 28 in  C:\Pester\Result.Tests.ps1'
            }
    
             It "should write the test summary" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $TestResults.AddTestResult("Testcase",'Passed',(New-TimeSpan -Seconds 1))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xmlResult = [xml] (Get-Content $testFile)
                $xmlTestResult = $xmlResult.'test-results'
                $xmlTestResult.total    | Should Be 1
                $xmlTestResult.failures | Should Be 0
                $xmlTestResult.date     | Should Be $true
                $xmlTestResult.time     | Should Be $true
            }
    
            it "should write the test-suite information" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
                $TestResults.AddTestResult("Successful testcase",'Passed',[timespan]10000000) #1.0 seconds
                $TestResults.AddTestResult("Successful testcase",'Passed',[timespan]11000000) #1.1 seconds
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xmlResult = [xml] (Get-Content $testFile)
    
                $xmlTestResult = $xmlResult.'test-results'.'test-suite'.results.'test-suite'
                $xmlTestResult.type            | Should Be "TestFixture"
                $xmlTestResult.name            | Should Be "Mocked Describe"
                $xmlTestResult.description     | Should Be "Mocked Describe"
                $xmlTestResult.result          | Should Be "Success"
                $xmlTestResult.success         | Should Be "True"
                $xmlTestResult.time            | Should Be 2.1
            }
    
            it "should write two test-suite elements for two describes" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Describe #1')
                $TestResults.AddTestResult("Successful testcase",'Passed',(New-TimeSpan -Seconds 1))
                $TestResults.LeaveDescribe()
                $testResults.EnterDescribe('Describe #2')
                $TestResults.AddTestResult("Failed testcase",'Failed',(New-TimeSpan -Seconds 2))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xmlResult = [xml] (Get-Content $testFile)
    
                $xmlTestSuite1 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[0]
                $xmlTestSuite1.name        | Should Be "Describe #1"
                $xmlTestSuite1.description | Should Be "Describe #1"
                $xmlTestSuite1.result      | Should Be "Success"
                $xmlTestSuite1.success     | Should Be "True"
                $xmlTestSuite1.time        | Should Be 1.0
    
                $xmlTestSuite2 = $xmlResult.'test-results'.'test-suite'.results.'test-suite'[1]
                $xmlTestSuite2.name        | Should Be "Describe #2"
                $xmlTestSuite2.description | Should Be "Describe #2"
                $xmlTestSuite2.result      | Should Be "Failure"
                $xmlTestSuite2.success     | Should Be "False"
                $xmlTestSuite2.time        | Should Be 2.0
            }
    
            it "should write the environment information" {
                $state = New-PesterState "."
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $state $testFile
                $xmlResult = [xml] (Get-Content $testFile)
    
                $xmlEnvironment = $xmlResult.'test-results'.'environment'
                $xmlEnvironment.'os-Version'    | Should Be $true
                $xmlEnvironment.platform        | Should Be $true
                $xmlEnvironment.cwd             | Should Be (Get-Location).Path
                if ($env:Username) {
                    $xmlEnvironment.user        | Should Be $env:Username
                }
                $xmlEnvironment.'machine-name'  | Should Be $env:ComputerName
            }
    
            it "Should validate test results against the nunit 2.5 schema" {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Describe #1')
                $TestResults.AddTestResult("Successful testcase",'Passed',(New-TimeSpan -Seconds 1))
                $TestResults.LeaveDescribe()
                $testResults.EnterDescribe('Describe #2')
                $TestResults.AddTestResult("Failed testcase",'Failed',(New-TimeSpan -Seconds 2))
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xml = [xml] (Get-Content $testFile)
    
                $schemePath = (Get-Module -Name Pester).Path | Split-Path | Join-Path -ChildPath "nunit_schema_2.5.xsd"
                $xml.Schemas.Add($null,$schemePath) > $null
                { $xml.Validate({throw $args.Exception }) } | Should Not Throw
            }
    
            it "handles special characters in block descriptions well [email protected]#$%^&*()_+`1234567890[];'',./""- " {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Describe [email protected]#$%^&*()_+`1234567890[];'',./"- #1')
                $TestResults.AddTestResult("Successful testcase [email protected]#$%^&*()_+`1234567890[];'',./""-",'Passed',(New-TimeSpan -Seconds 1))
                $TestResults.LeaveDescribe()
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xml = [xml] (Get-Content $testFile)
    
                $schemePath = (Get-Module -Name Pester).Path | Split-Path | Join-Path -ChildPath "nunit_schema_2.5.xsd"
                $xml.Schemas.Add($null,$schemePath) > $null
                { $xml.Validate({throw $args.Exception }) } | Should Not Throw
            }
    
            Context 'Exporting Parameterized Tests (Newer format)' {
                #create state
                $TestResults = New-PesterState -Path TestDrive:\
                $testResults.EnterDescribe('Mocked Describe')
    
                $TestResults.AddTestResult(
                    'Parameterized Testcase One',
                    'Passed',
                    (New-TimeSpan -Seconds 1),
                    $null,
                    $null,
                    'Parameterized Testcase <A>',
                    @{Parameter = 'One'}
                )
    
                $parameters = New-Object System.Collections.Specialized.OrderedDictionary
                $parameters.Add('StringParameter', 'Two')
                $parameters.Add('NullParameter', $null)
                $parameters.Add('NumberParameter', -42.67)
    
                $TestResults.AddTestResult(
                    'Parameterized Testcase <A>',
                    'Failed',
                    (New-TimeSpan -Seconds 1),
                    'Assert failed: "Expected: Test. But was: Testing"',
                    'at line: 28 in  C:\Pester\Result.Tests.ps1',
                    'Parameterized Testcase <A>',
                    $parameters
                )
    
                #export and validate the file
                $testFile = "$TestDrive\Results\Tests.xml"
                Export-NunitReport $testResults $testFile
                $xmlResult    = [xml] (Get-Content $testFile)
    
                It 'should write parameterized test results correctly' {
                    $xmlTestSuite = $xmlResult.'test-results'.'test-suite'.'results'.'test-suite'.'results'.'test-suite'
    
                    $xmlTestSuite.name        | Should Be 'Mocked Describe.Parameterized Testcase <A>'
                    $xmlTestSuite.description | Should Be 'Parameterized Testcase <A>'
                    $xmlTestSuite.type        | Should Be 'ParameterizedTest'
                    $xmlTestSuite.result      | Should Be 'Failure'
                    $xmlTestSuite.success     | Should Be 'False'
                    $xmlTestSuite.time        | Should Be '2'
    
                    $testCase1 = $xmlTestSuite.results.'test-case'[0]
                    $testCase2 = $xmlTestSuite.results.'test-case'[1]
    
                    $testCase1.Name | Should Be 'Mocked Describe.Parameterized Testcase One'
                    $testCase1.Time | Should Be 1
    
                    $testCase2.Name | Should Be 'Mocked Describe.Parameterized Testcase <A>("Two",null,-42.67)'
                    $testCase2.Time | Should Be 1
                }
    
                it 'Should validate test results against the nunit 2.5 schema' {
                    $schemaPath = (Get-Module -Name Pester).Path | Split-Path | Join-Path -ChildPath "nunit_schema_2.5.xsd"
                    $null = $xmlResult.Schemas.Add($null,$schemaPath)
                    { $xmlResult.Validate({throw $args.Exception }) } | Should Not Throw
                }
            }
        }
    
        Describe "Get-TestTime" {
            function Using-Culture {
                param (
                    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
                    [ScriptBlock]$ScriptBlock,
                    [System.Globalization.CultureInfo]$Culture='en-US'
                )
    
                $oldCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
                try
                {
                    [System.Threading.Thread]::CurrentThread.CurrentCulture = $Culture
                    $ExecutionContext.InvokeCommand.InvokeScript($ScriptBlock)
                }
                finally
                {
                    [System.Threading.Thread]::CurrentThread.CurrentCulture = $oldCulture
                }
            }
    
            It "output is culture agnostic" {
                #on cs-CZ, de-DE and other systems where decimal separator is ",". value [double]3.5 is output as 3,5
                #this makes some of the tests fail, it could also leak to the nUnit report if the time was output
    
                $TestResult = New-Object -TypeName psObject -Property @{ Time = [timespan]35000000 } #3.5 seconds
    
                #using the string formatter here to know how the string will be output to screen
                $Result = { Get-TestTime -Tests $TestResult | Out-String -Stream } | Using-Culture -Culture de-DE
                $Result | Should Be "3.5"
            }
            It "Time is measured in seconds with 0,1 millisecond as lowest value" {
                $TestResult = New-Object -TypeName psObject -Property @{ Time = [timespan]1000 }
                Get-TestTime -Tests $TestResult | Should Be 0.0001
                $TestResult = New-Object -TypeName psObject -Property @{ Time = [timespan]100 }
                Get-TestTime -Tests $TestResult | Should Be 0
                $TestResult = New-Object -TypeName psObject -Property @{ Time = [timespan]1234567 }
                Get-TestTime -Tests $TestResult | Should Be 0.1235
            }
        }
    
        Describe "GetFullPath" {
            It "Resolves non existing path correctly" {
                pushd TestDrive:\
                $p = GetFullPath notexistingfile.txt
                popd
                $p | Should Be (Join-Path $TestDrive notexistingfile.txt)
            }
    
            It "Resolves existing path correctly" {
                pushd TestDrive:\
                New-Item -ItemType File -Name existingfile.txt
                $p = GetFullPath existingfile.txt
                popd
                $p | Should Be (Join-Path $TestDrive existingfile.txt)
            }
    
            It "Resolves full path correctly" {
                GetFullPath C:\Windows\System32\notepad.exe | Should Be C:\Windows\System32\notepad.exe
            }
        }
    }
    
  • tools\Functions\TestsRunningInCleanRunspace.Tests.ps1 Show
    function Invoke-PesterInJob ($ScriptBlock)
    {        
    	#TODO: there must be a safer way to determine this while I am in describe
    	$PesterPath = Get-Module -Name Pester | Select -First 1 -ExpandProperty Path
    	
    	$job = Start-Job { 
    		param ($PesterPath, $TestDrive, $ScriptBlock) 
    		Import-Module $PesterPath -Force | Out-Null
    		$ScriptBlock | Set-Content $TestDrive\Temp.Tests.ps1 | Out-Null
    		
    		Invoke-Pester -PassThru -Path $TestDrive
    		
    	} -ArgumentList  $PesterPath, $TestDrive, $ScriptBlock 
    	$job | Wait-Job | Out-Null 
    	
    	#not using Recieve-Job to ignore any output to Host
    	#TODO: how should this handle errors?
    	#$job.Error | foreach { throw $_.Exception  } 
    	$job.Output
    	$job.ChildJobs| foreach { 
    		$childJob = $_ 
    		#$childJob.Error | foreach { throw $_.Exception }
    		$childJob.Output 
    	}
    	$job | Remove-Job
    }
    
    Describe "Tests running in clean runspace" {   
        It "It - Skip and Pending tests" {
            #tests to be run in different runspace using different Pester instance
            $TestSuite = {
                Describe 'It - Skip and Pending tests' {
                   
                    It "Skip without ScriptBlock" -skip
                    It "Skip with empty ScriptBlock" -skip {}
                    It "Skip with not empty ScriptBlock" -Skip {"something"}
                    
                    It "Implicit pending" {}
                    It "Pending without ScriptBlock" -Pending
                    It "Pending with empty ScriptBlock" -Pending {}
                    It "Pending with not empty ScriptBlock" -Pending {"something"} 
                }
            }
            
            $result = Invoke-PesterInJob -ScriptBlock $TestSuite 
            $result.SkippedCount | Should Be 3
            $result.PendingCount | Should Be 4
            $result.TotalCount | Should Be 7
        }
        
        It "It - It without ScriptBlock fails" {
            #tests to be run in different runspace using different Pester instance
            $TestSuite = {
                Describe 'It without ScriptBlock fails' {
                   It "Fails whole describe"
                   It "is not run" { "but it would pass if it was run" }
                   
                }
            }
            
            $result = Invoke-PesterInJob -ScriptBlock $TestSuite 
            $result.PassedCount | Should Be 0
            $result.FailedCount | Should Be 1
            
            $result.TotalCount | Should Be 1
        }
        
        It "Invoke-Pester - PassThru output" {
            #tests to be run in different runspace using different Pester instance
            $TestSuite = {
                Describe 'PassThru output' {
                   it "Passes" { "pass" }
                   it "fails" { throw }
                   it "Skipped" -Skip {}
                   it "Pending" -Pending {}
                }
            }
            
            $result = Invoke-PesterInJob -ScriptBlock $TestSuite 
            $result.PassedCount | Should Be 1
            $result.FailedCount | Should Be 1
            $result.SkippedCount | Should Be 1
            $result.PendingCount | Should Be 1
            
            $result.TotalCount | Should Be 4
        }
    }
  • tools\nunit_schema_2.5.xsd
  • tools\Pester.psd1
  • tools\Pester.psm1 Show
    # Pester
    # Version: 3.3.1
    # Changeset: 
    
    $moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path
    
    "$moduleRoot\Functions\*.ps1", "$moduleRoot\Functions\Assertions\*.ps1" |
    Resolve-Path |
    Where-Object { -not ($_.ProviderPath.ToLower().Contains(".tests.")) } |
    ForEach-Object { . $_.ProviderPath }
    
    function Invoke-Pester {
    <#
    .SYNOPSIS
    Invokes Pester to run all tests (files containing *.Tests.ps1) recursively under the Path
    
    .DESCRIPTION
    Upon calling Invoke-Pester. All files that have a name containing
    "*.Tests.ps1" will have there tests defined in their Describe blocks
    executed. Invoke-Pester begins at the location of Path and
    runs recursively through each sub directory looking for
    *.Tests.ps1 files for tests to run. If a TestName is provided,
    Invoke-Pester will only run tests that have a describe block with a
    matching name. By default, Invoke-Pester will end the test run with a
    simple report of the number of tests passed and failed output to the
    console. One may want pester to "fail a build" in the event that any
    tests fail. To accomodate this, Invoke-Pester will return an exit
    code equal to the number of failed tests if the EnableExit switch is
    set. Invoke-Pester will also write a NUnit style log of test results
    if the OutputXml parameter is provided. In these cases, Invoke-Pester
    will write the result log to the path provided in the OutputXml
    parameter.
    
    Optionally, Pester can generate a report of how much code is covered
    by the tests, and information about any commands which were not
    executed.
    
    .PARAMETER Path
    The path where Invoke-Pester begins to search for test files. The default is the current directory. Aliased 'relative_path' for backwards compatibility.
    
    .PARAMETER TestName
    Informs Invoke-Pester to only run Describe blocks that match this name.
    
    .PARAMETER EnableExit
    Will cause Invoke-Pester to exit with a exit code equal to the number of failed tests once all tests have been run. Use this to "fail" a build when any tests fail.
    
    .PARAMETER OutputXml
    The path where Invoke-Pester will save a NUnit formatted test results log file. If this path is not provided, no log will be generated.
    
    .PARAMETER Tag
    Informs Invoke-Pester to only run Describe blocks tagged with the tags specified. Aliased 'Tags' for backwards compatibility.
    
    .PARAMETER ExcludeTag
    Informs Invoke-Pester to not run blocks tagged with the tags specified.
    
    .PARAMETER PassThru
    Returns a Pester result object containing the information about the whole test run, and each test.
    
    .PARAMETER CodeCoverage
    Instructs Pester to generate a code coverage report in addition to running tests.  You may pass either hashtables or strings to this parameter.
    If strings are used, they must be paths (wildcards allowed) to source files, and all commands in the files are analyzed for code coverage.
    By passing hashtables instead, you can limit the analysis to specific lines or functions within a file.
    Hashtables must contain a Path key (which can be abbreviated to just "P"), and may contain Function (or "F"), StartLine (or "S"), and EndLine ("E") keys to narrow down the commands to be analyzed.
    If Function is specified, StartLine and EndLine are ignored.
    If only StartLine is defined, the entire script file starting with StartLine is analyzed.
    If only EndLine is present, all lines in the script file up to and including EndLine are analyzed.
    Both Function and Path (as well as simple strings passed instead of hashtables) may contain wildcards.
    
    .PARAMETER Strict
    Makes Pending and Skipped tests to Failed tests. Useful for continuous integration where you need to make sure all tests passed.
    
    .PARAMETER Quiet
    Disables the output Pester writes to screen. No other output is generated unless you specify PassThru, or one of the Output parameters.
    
    .Example
    Invoke-Pester
    
    This will find all *.Tests.ps1 files and run their tests. No exit code will be returned and no log file will be saved.
    
    .Example
    Invoke-Pester ./tests/Utils*
    
    This will run all tests in files under ./Tests that begin with Utils and alsocontains .Tests.
    
    .Example
    Invoke-Pester -TestName "Add Numbers"
    
    This will only run the Describe block named "Add Numbers"
    
    .Example
    Invoke-Pester -EnableExit -OutputXml "./artifacts/TestResults.xml"
    
    This runs all tests from the current directory downwards and writes the results according to the NUnit schema to artifatcs/TestResults.xml just below the current directory. The test run will return an exit code equal to the number of test failures.
    
    .EXAMPLE
    Invoke-Pester -CodeCoverage 'ScriptUnderTest.ps1'
    
    Runs all *.Tests.ps1 scripts in the current directory, and generates a coverage report for all commands in the "ScriptUnderTest.ps1" file.
    
    .EXAMPLE
    Invoke-Pester -CodeCoverage @{ Path = 'ScriptUnderTest.ps1'; Function = 'FunctionUnderTest' }
    
    Runs all *.Tests.ps1 scripts in the current directory, and generates a coverage report for all commands in the "FunctionUnderTest" function in the "ScriptUnderTest.ps1" file.
    
    .EXAMPLE
    Invoke-Pester -CodeCoverage @{ Path = 'ScriptUnderTest.ps1'; StartLine = 10; EndLine = 20 }
    
    Runs all *.Tests.ps1 scripts in the current directory, and generates a coverage report for all commands on lines 10 through 20 in the "ScriptUnderTest.ps1" file.
    
    .LINK
    Describe
    about_pester
    
    #>
        [CmdletBinding(DefaultParameterSetName = 'LegacyOutputXml')]
        param(
            [Parameter(Position=0,Mandatory=0)]
            [Alias('relative_path')]
            [string]$Path = ".",
            [Parameter(Position=1,Mandatory=0)]
            [Alias("Name")]
            [string[]]$TestName,
            [Parameter(Position=2,Mandatory=0)]
            [switch]$EnableExit,
            [Parameter(Position=3,Mandatory=0, ParameterSetName = 'LegacyOutputXml')]
            [string]$OutputXml,
    
            [Parameter(Position=4,Mandatory=0)]
            [Alias('Tags')]
            [string[]]$Tag,
    
            [string[]]$ExcludeTag,
    
            [switch]$PassThru,
    
            [object[]] $CodeCoverage = @(),
            [Switch]$Strict,
    
            [Parameter(Mandatory = $true, ParameterSetName = 'NewOutputSet')]
            [string] $OutputFile,
    
            [Parameter(Mandatory = $true, ParameterSetName = 'NewOutputSet')]
            [ValidateSet('LegacyNUnitXml', 'NUnitXml')]
            [string] $OutputFormat,
            [Switch]$Quiet
        )
    
        if ($PSBoundParameters.ContainsKey('OutputXml'))
        {
            Write-Warning 'The -OutputXml parameter has been deprecated; please use the new -OutputFile and -OutputFormat parameters instead.  To get the same type of export that the -OutputXml parameter currently provides, use an -OutputFormat of "LegacyNUnitXml".'
    
            Start-Sleep -Seconds 2
    
            $OutputFile = $OutputXml
            $OutputFormat = 'LegacyNUnitXml'
        }
    
        $script:mockTable = @{}
    
        $pester = New-PesterState -Path (Resolve-Path $Path) -TestNameFilter $TestName -TagFilter ($Tag -split "\s") -ExcludeTagFilter ($ExcludeTag -split "\s") -SessionState $PSCmdlet.SessionState -Strict:$Strict -Quiet:$Quiet
        Enter-CoverageAnalysis -CodeCoverage $CodeCoverage -PesterState $pester
    
        $message = "Executing all tests in '$($pester.Path)'"
        if ($TestName) { $message += " matching test name '$TestName'" }
    
        Write-Screen $message
    
        $scriptBlock = { & $args[0] }
        Set-ScriptBlockScope -ScriptBlock $scriptBlock -SessionState $PSCmdlet.SessionState
    
        Get-ChildItem $pester.Path -Filter "*.Tests.ps1" -Recurse |
        where { -not $_.PSIsContainer } |
        foreach {
            $testFile = $_
    
            try
            {
                & $scriptBlock $testFile.PSPath
            }
            catch
            {
                $firstStackTraceLine = $_.ScriptStackTrace -split '\r?\n' | Select-Object -First 1
                $pester.AddTestResult("Error occurred in test script '$($testFile.FullName)'", "Failed", $null, $_.Exception.Message, $firstStackTraceLine)
                $pester.TestResult[-1] | Write-PesterResult
            }
        }
    
        $pester | Write-PesterReport
        $coverageReport = Get-CoverageReport -PesterState $pester
        Show-CoverageReport -CoverageReport $coverageReport
        Exit-CoverageAnalysis -PesterState $pester
    
    
        if($OutputFile) {
            Export-PesterResults -PesterState $pester -Path $OutputFile -Format $OutputFormat
        }
    
        if ($PassThru) {
            #remove all runtime properties like current* and Scope
            $properties = @(
                "Path","TagFilter","ExcludeTagFilter","TestNameFilter","TotalCount","PassedCount","FailedCount","SkippedCount","PendingCount","Time","TestResult"
    
                if ($CodeCoverage)
                {
                    @{ Name = 'CodeCoverage'; Expression = { $coverageReport } }
                }
            )
    
            $pester | Select -Property $properties
        }
    
        if ($EnableExit) { Exit-WithCode -FailedCount $pester.FailedCount }
    }
    
    function Set-ScriptBlockScope
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock,
    
            [Parameter(Mandatory = $true, ParameterSetName = 'FromSessionState')]
            [System.Management.Automation.SessionState]
            $SessionState,
    
            [Parameter(Mandatory = $true, ParameterSetName = 'FromSessionStateInternal')]
            $SessionStateInternal
        )
    
        $flags = [System.Reflection.BindingFlags]'Instance,NonPublic'
    
        if ($PSCmdlet.ParameterSetName -eq 'FromSessionState')
        {
            $SessionStateInternal = $SessionState.GetType().GetProperty('Internal', $flags).GetValue($SessionState, $null)
        }
    
        [scriptblock].GetProperty('SessionStateInternal', $flags).SetValue($ScriptBlock, $SessionStateInternal, $null)
    }
    
    function Get-ScriptBlockScope
    {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory = $true)]
            [scriptblock]
            $ScriptBlock
        )
    
        $flags = [System.Reflection.BindingFlags]'Instance,NonPublic'
        [scriptblock].GetProperty('SessionStateInternal', $flags).GetValue($ScriptBlock, $null)
    }
    
    function Get-IgnoreErrorPreference
    {
        if ($PSVersionTable.PSVersion.Major -ge 3)
        {
            return 'Ignore'
        }
        else
        {
            return 'SilentlyContinue'
        }
    
    }
    
    if (($null -ne $psISE) -and ($PSVersionTable.PSVersion.Major -ge 3))
    {
        Import-IseSnippet -Path $PSScriptRoot\Snippets
    }
    
    Export-ModuleMember Describe, Context, It, In, Mock, Assert-VerifiableMocks, Assert-MockCalled
    Export-ModuleMember New-Fixture, Get-TestDriveItem, Should, Invoke-Pester, Setup, InModuleScope, Invoke-Mock
    Export-ModuleMember BeforeEach, AfterEach, BeforeAll, AfterAll
    Export-ModuleMember Get-MockDynamicParameters, Set-DynamicParameterVariables
    Export-ModuleMember Get-IgnoreErrorPreference
    
  • tools\Pester.Tests.ps1 Show
    $here = Split-Path -Parent $MyInvocation.MyCommand.Path
    
    $manifestPath   = "$here\Pester.psd1"
    $changellogPath = "$here\CHANGELOG.md"
    
    Describe "Pester manifest and changelog" {    
        $script:manifest = $null
        It "has a valid manifest" {
            {   
                $script:manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction Stop -WarningAction SilentlyContinue
            } | Should Not Throw
        }
        
        It "has a valid name in the manifest" {
            $script:manifest.Name | Should Be Pester
        }
        
        It "has a valid guid in the manifest" {
            $script:manifest.Guid | Should Be 'a699dea5-2c73-4616-a270-1f7abb777e71'
        }
        
        It "has a valid version in the manifest" {
            $script:manifest.Version -as [Version] | Should Not BeNullOrEmpty
        }
        
        $script:changelogVersion = $null
        It "has a valid version in the changelog" {
            
            foreach ($line in (Get-Content $changellogPath)) 
            {
                if ($line -match "^\D*(?<Version>(\d+\.){1,3}\d+)")  
                {
                    $script:changelogVersion = $matches.Version
                    break
                }
            }
            $script:changelogVersion                | Should Not BeNullOrEmpty
            $script:changelogVersion -as [Version]  | Should Not BeNullOrEmpty
        }
        
        It "changelog and manifest versions are the same" {
            $script:changelogVersion -as [Version] | Should be ( $script:manifest.Version -as [Version] )
        }
        
        if (Get-Command git.exe -ErrorAction SilentlyContinue ) {
            $script:tagVersion
            It "is tagged with a valid version" {
                $script:tagVersion = git describe --abbrev=0 --tags 
                $script:tagVersion                  | Should Not BeNullOrEmpty
                $script:tagVersion -as [Version]    | Should Not BeNullOrEmpty
            }
            
            It "all versions are the same" {
                $script:changelogVersion -as [Version] | Should be ( $script:manifest.Version -as [Version] )
                $script:manifest.Version -as [Version] | Should be ( $script:tagVersion -as [Version] )
            }
            
        }
    }
    
    if ($PSVersionTable.PSVersion.Major -ge 3)
    {
        $error.Clear()
        Describe 'Clean treatment of the $error variable' {
            Context 'A Context' {
                It 'Performs a successful test' {
                    $true | Should Be $true
                }
            }
    
            It 'Did not add anything to the $error variable' {
                $error.Count | Should Be 0
            }
        }
    }
    
  • tools\Functions\InModuleScope.Tests.ps1 Show
    Set-StrictMode -Version Latest
    
    Describe "Module scope separation" {
        Context "When users define variables with the same name as Pester parameters" {
            $test = "This is a test."
    
            It "does not hide user variables" {
                $test | Should Be 'This is a test.'
            }
        }
    
        It "Does not expose Pester implementation details to the SUT" {
            # Changing the Get-PesterResult function's name would cause this test to pass artificially.
            # TODO : come up with a better way of verifying that only the desired commands from the Pester
            # module are visible to the SUT.
    
            (Get-Item function:\Get-PesterResult -ErrorAction SilentlyContinue) | Should Be $null
        }
    }
    
    Describe "Executing test code inside a module" {
        New-Module -Name TestModule {
            function InternalFunction { 'I am the internal function' }
            function PublicFunction   { InternalFunction }
            Export-ModuleMember -Function PublicFunction
        } | Import-Module -Force
    
        It "Cannot call module internal functions, by default" {
            { InternalFunction } | Should Throw
        }
    
        InModuleScope TestModule {
            It "Can call module internal functions using InModuleScope" {
                InternalFunction | Should Be 'I am the internal function'
            }
    
            It "Can mock functions inside the module without using Mock -ModuleName" {
                Mock InternalFunction { 'I am the mock function.' }
                InternalFunction | Should Be 'I am the mock function.'
            }
        }
    
        Remove-Module TestModule -Force
    }
    
  • tools\Functions\It.ps1 Show
    function It {
    <#
    .SYNOPSIS
    Validates the results of a test inside of a Describe block.
    
    .DESCRIPTION
    The It command is intended to be used inside of a Describe or Context Block.
    If you are familiar with the AAA pattern (Arrange-Act-Assert), the body of
    the It block is the appropriate location for an assert. The convention is to
    assert a single expectation for each It block. The code inside of the It block
    should throw a terminating error if the expectation of the test is not met and
    thus cause the test to fail. The name of the It block should expressively state
    the expectation of the test.
    
    In addition to using your own logic to test expectations and throw exceptions,
    you may also use Pester's Should command to perform assertions in plain language.
    
    .PARAMETER Name
    An expressive phsae describing the expected test outcome.
    
    .PARAMETER Test
    The script block that should throw an exception if the
    expectation of the test is not met.If you are following the
    AAA pattern (Arrange-Act-Assert), this typically holds the
    Assert.
    
    .PARAMETER Pending
    Marks the test as pending, that is inconclusive/not implemented. The test will not run and will
    
    .PARAMETER TestCases
    Optional array of hashtable (or any IDictionary) objects.  If this parameter is used,
    Pester will call the test script block once for each table in the TestCases array,
    splatting the dictionary to the test script block as input.  If you want the name of
    the test to appear differently for each test case, you can embed tokens into the Name
    parameter with the syntax 'Adds numbers <A> and <B>' (assuming you have keys named A and B
    in your TestCases hashtables.)
    
    .EXAMPLE
    function Add-Numbers($a, $b) {
        return $a + $b
    }
    
    Describe "Add-Numbers" {
        It "adds positive numbers" {
            $sum = Add-Numbers 2 3
            $sum | Should Be 5
        }
    
        It "adds negative numbers" {
            $sum = Add-Numbers (-2) (-2)
            $sum | Should Be (-4)
        }
    
        It "adds one negative number to positive number" {
            $sum = Add-Numbers (-2) 2
            $sum | Should Be 0
        }
    
        It "concatenates strings if given strings" {
            $sum = Add-Numbers two three
            $sum | Should Be "twothree"
        }
    }
    
    .EXAMPLE
    function Add-Numbers($a, $b) {
        return $a + $b
    }
    
    Describe "Add-Numbers" {
        $testCases = @(
            @{ a = 2;     b = 3;       expectedResult = 5 }
            @{ a = -2;    b = -2;      expectedResult = -4 }
            @{ a = -2;    b = 2;       expectedResult = 0 }
            @{ a = 'two'; b = 'three'; expectedResult = 'twothree' }
        )
    
        It 'Correctly adds <a> and <b> to get <expectedResult>' -TestCases $testCases {
            param ($a, $b, $expectedResult)
    
            $sum = Add-Numbers $a $b
            $sum | Should Be $expectedResult
        }
    }
    
    .LINK
    Describe
    Context
    about_should
    #>
        [CmdletBinding(DefaultParameterSetName = 'Normal')]
        param(
            [Parameter(Mandatory = $true, Position = 0)]
            [string]$name,
    
            [Parameter(Position = 1)]
            [ScriptBlock] $test = {},
    
            [System.Collections.IDictionary[]] $TestCases,
    
            [Parameter(ParameterSetName = 'Pending')]
            [Switch] $Pending,
    
            [Parameter(ParameterSetName = 'Skip')]
            [Switch] $Skip
        )
    
        ItImpl -Pester $pester -OutputScriptBlock ${function:Write-PesterResult} @PSBoundParameters
    }
    
    function ItImpl
    {
        [CmdletBinding(DefaultParameterSetName = 'Normal')]
        param(
            [Parameter(Mandatory = $true, Position=0)]
            [string]$name,
            [Parameter(Position = 1)]
            [ScriptBlock] $test,
            [System.Collections.IDictionary[]] $TestCases,
            [Parameter(ParameterSetName = 'Pending')]
            [Switch] $Pending,
    
            [Parameter(ParameterSetName = 'Skip')]
            [Switch] $Skip,
    
            $Pester,
            [scriptblock] $OutputScriptBlock
        )
    
        Assert-DescribeInProgress -CommandName It
    
        #unless Skip or Pending is specified you must specify a ScriptBlock to the Test parameter
        if (-not ($PSBoundParameters.ContainsKey('test') -or $Skip -or $Pending))
        {
            throw 'No test script block is provided. (Have you put the open curly brace on the next line?)'
        }
    
        #the function is called with Pending or Skipped set the script block if needed
        if ($null -eq $test) { $test = {} }
    
        #mark empty Its as Pending
        #[String]::IsNullOrWhitespace is not available in .NET version used with PowerShell 2
        if ($PSCmdlet.ParameterSetName -eq 'Normal' -and
           [String]::IsNullOrEmpty((Remove-Comments $test.ToString()) -replace "\s"))
        {
            $Pending = $true
        }
    
        $pendingSkip = @{}
    
        if ($PSCmdlet.ParameterSetName -eq 'Skip')
        {
            $pendingSkip['Skip'] = $Skip
        }
        else
        {
            $pendingSkip['Pending'] = $Pending
        }
    
        if ($null -ne $TestCases -and $TestCases.Count -gt 0)
        {
            foreach ($testCase in $TestCases)
            {
                $expandedName = [regex]::Replace($name, '<([^>]+)>', {
                    $capture = $args[0].Groups[1].Value
                    if ($testCase.Contains($capture))
                    {
                        $testCase[$capture]
                    }
                    else
                    {
                        "<$capture>"
                    }
                })
    
                $splat = @{
                    Name = $expandedName
                    Scriptblock = $test
                    Parameters = $testCase
                    ParameterizedSuiteName = $name
                    OutputScriptBlock = $OutputScriptBlock
                }
    
                Invoke-Test @splat @pendingSkip
            }
        }
        else
        {
            Invoke-Test -Name $name -ScriptBlock $test @pendingSkip -OutputScriptBlock $OutputScriptBlock
        }
    }
    
    function Invoke-Test
    {
        [CmdletBinding(DefaultParameterSetName = 'Normal')]
        param (
            [Parameter(Mandatory = $true)]
            [string] $Name,
    
            [Parameter(Mandatory = $true)]
            [ScriptBlock] $ScriptBlock,
    
            [scriptblock] $OutputScriptBlock,
    
            [System.Collections.IDictionary] $Parameters,
            [string] $ParameterizedSuiteName,
    
            [Parameter(ParameterSetName = 'Pending')]
            [Switch] $Pending,
    
            [Parameter(ParameterSetName = 'Skip')]
            [Switch] $Skip
        )
    
        if ($null -eq $Parameters) { $Parameters = @{} }
    
        $Pester.EnterTest($Name)
    
        if ($Skip)
        {
            $Pester.AddTestResult($Name, "Skipped", $null)
        }
        elseif ($Pending)
        {
            $Pester.AddTestResult($Name, "Pending", $null)
        }
        else
        {
            Invoke-TestCaseSetupBlocks
    
            $PesterException = $null
            try{
                $null = & $ScriptBlock @Parameters
            } catch {
                $PesterException = $_
            }
    
            $result = Get-PesterResult -Test $ScriptBlock -Exception $PesterException
            $orderedParameters = Get-OrderedParameterDictionary -ScriptBlock $ScriptBlock -Dictionary $Parameters
            $Pester.AddTestResult( $result.name, $result.Result, $null, $result.FailureMessage, $result.StackTrace, $ParameterizedSuiteName, $orderedParameters )
        }
    
        if ($null -ne $OutputScriptBlock)
        {
            $Pester.testresult[-1] | & $OutputScriptBlock
        }
    
        if (-not ($Skip -or $Pending))
        {
            Invoke-TestCaseTeardownBlocks
        }
    
        Exit-MockScope
        $Pester.LeaveTest()
    }
    
    function Get-PesterResult {
        param([ScriptBlock] $Test, $Time, $Exception)
        $testResult = @{
            name = $name
            time = $time
            failureMessage = ""
            stackTrace = ""
            success = $false
            result = "Failed"
        };
    
        if(-not $exception)
        {
            $testResult.Result = "Passed"
            $testResult.success = $true
            return $testResult
        }
    
        if ($exception.FullyQualifiedErrorID -eq 'PesterAssertionFailed')
        {
            $failureMessage = $exception.exception.message
            $file = $test.File
            $line = if ( $exception.ErrorDetails.message -match "\d+$" )  { $matches[0] }
        }
        else {
            $failureMessage = $exception.ToString()
            $file = $Exception.InvocationInfo.ScriptName
            $line = $Exception.InvocationInfo.ScriptLineNumber
        }
    
        $testResult.failureMessage = $failureMessage -replace "Exception calling", "Assert failed on"
        $testResult.stackTrace = "at line: $line in $file"
    
        return $testResult
    }
    
    function Remove-Comments ($Text)
    {
        $text -replace "(?s)(<#.*#>)" -replace "\#.*"
    }
    
    function Get-OrderedParameterDictionary
    {
        [OutputType([System.Collections.IDictionary])]
        param (
            [scriptblock] $ScriptBlock,
            [System.Collections.IDictionary] $Dictionary
        )
    
        $parameters = Get-ParameterDictionary -ScriptBlock $ScriptBlock
    
        $orderedDictionary = New-Object System.Collections.Specialized.OrderedDictionary
    
        foreach ($parameterName in $parameters.Keys)
        {
            $value = $null
            if ($Dictionary.ContainsKey($parameterName))
            {
                $value = $Dictionary[$parameterName]
            }
    
            $orderedDictionary[$parameterName] = $value
        }
    
        return $orderedDictionary
    }
    
    function Get-ParameterDictionary
    {
        param (
            [scriptblock] $ScriptBlock
        )
    
        $guid = [guid]::NewGuid().Guid
    
        try
        {
            Set-Content function:\$guid $ScriptBlock
            $metadata = [System.Management.Automation.CommandMetadata](Get-Command -Name $guid -CommandType Function)
    
            return $metadata.Parameters
        }
        finally
        {
            if (Test-Path function:\$guid) { Remove-Item function:\$guid }
        }
    }
    

Virus Scan Results

In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).

Chocolatey Pro provides runtime protection from possible malware.

Dependencies

This package has no dependencies.

Package Maintainer(s)

Software Author(s)

  • Pester Team

Tags

Version History

Version Downloads Last updated Status
Pester 4.7.2 1174 Friday, March 8, 2019 approved
Pester 4.7.1 396 Tuesday, March 5, 2019 approved
Pester 4.7.0 226 Sunday, March 3, 2019 approved
Pester 4.4.1 8749 Thursday, September 20, 2018 approved
Pester 4.4.0 4647 Friday, July 20, 2018 approved
Pester 4.4.0-beta2 72 Sunday, July 8, 2018 approved
Pester 4.4.0-beta 189 Sunday, May 6, 2018 approved
Pester 4.3.1 7298 Tuesday, February 20, 2018 approved
Pester 4.3.0 124 Tuesday, February 20, 2018 approved
Show More

Discussion for the Pester Package

Ground rules:

  • This discussion is only about Pester and the Pester package. If you have feedback for Chocolatey, please contact the google group.
  • This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
  • The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
  • Tell us what you love about the package or Pester, or tell us what needs improvement.
  • Share your experiences with the package, or extra configuration or gotchas that you've found.
  • If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.

comments powered by Disqus
Chocolatey.org uses cookies to enhance the user experience of the site.
Ok