Pester ignores mock

203 Views Asked by At

Given my powershell code (not in a function, as the last few parts are the script running, but i want to check some stuff for my script, using pester) My powershell code WinVersion.ps1

$WindowsVersion = Get-CimInstance -ClassName Win32_Operatingsystem | select -expand Caption

My Pester script:

BeforeAll {
    . $PSCommandPath.Replace('.Tests.ps1', '.ps1')
}

Describe "Test Server 2012" {
    It "Given Server 2012, return correct data" {
        Mock -CommandName Get-CimInstance -ParameterFilter {$ClassName -eq "Win32_Operatingsystem"} -MockWith {
            Write-Host "CIM"
            return [Microsoft.Management.Infrastructure.CimInstanc]@{
                Caption = "Microsoft Windows Server 2012 Datacenter"
            }
        }
        write-host $WindowsVersion
    }
}

My Write-host (in my pester script) should return Microsoft Windows Server 2012 Datacenter but it returns my own windows version. It thus ignores my mock.

I also tried the mock without the ParameterFilter, but that also didn't work.

This is the result that Pester gives me:

Starting discovery in 1 files.
Discovery found 2 tests in 82ms.
Running tests.
[-] Test Server 2012.Given Server 2012, return correct data 46ms (45ms|1ms)
 Expected strings to be the same, but they were different.
 Expected length: 40
 Actual length:   31
 Strings differ at index 18.
 Expected: 'Microsoft Windows Server 2012 Datacenter'
 But was:  'Microsoft Windows 11 Enterprise'
            ------------------^
 at $WindowsVersion | Should -Be "Microsoft Windows Server 2012 Datacenter"

Any clue why?

1

There are 1 best solutions below

0
On

A few things:

  1. The script being tested is being run before your Mock is defined so Get-CimInstance will not be mocked.
  2. Does [Microsoft.Management.Infrastructure.CimInstanc]@{ Caption = "Microsoft Windows Server 2012 Datacenter"} actually work on your computer? If you copy and paste that into PowerShell do you get an instance of that object back? You could just use a pscustomobject in its place [pscustomobject]@{Caption = "Microsoft Windows Server 2012 Datacenter"}

There are two options I can think of to resolve this.

Quick fix is to just move where you are dot sourcing your script at

Describe 'Test Server 2012' {
    BeforeAll {
        Mock -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'Win32_Operatingsystem' } -MockWith {
            [pscustomobject]@{Caption = 'Microsoft Windows Server 2012 Datacenter' }
        }

        # source the script here after mock is defined but before tests are run 
        . $PSScriptRoot\somescript.ps1
    }

    It 'Given Server 2012, return correct data' {
        $WindowsVersion | Should -Be 'Microsoft Windows Server 2012 Datacenter'
    }
}

A better fix might be to reconsider how to go about testing. Defining functions in your script allows you to test more easily

BeforeAll {
    # Everything in this block executes before the tests are set up and ran

    # This command dot sources the script we are testing
    # . $PSCommandPath.Replace('.Tests.ps1', '.ps1')

    # Defining functions that can be run in our tests.  
    # This could instead be defined in the script that we are testing

    function Test-Mock {
        Get-CimInstance -ClassName Win32_Operatingsystem | Select-Object -ExpandProperty Caption
    }

    function Test-UnaffectedByMock{
        Get-CimInstance -ClassName Win32_Volume | Select-Object -ExpandProperty Caption
    }
}

Describe 'Test Server 2012' {
    BeforeAll {
        Mock -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'Win32_Operatingsystem' } -MockWith {
            [pscustomobject]@{Caption = 'Microsoft Windows Server 2012 Datacenter' }
        }
    }

    It 'Given Server 2012, return correct data' {
        Test-Mock | Should -Be 'Microsoft Windows Server 2012 Datacenter'
    }

    It 'Supplying different ClassName should not run mock' {
        Test-UnaffectedByMock | Should -Not -Be 'Microsoft Windows Server 2012 Datacenter'
    }
}