How do I correctly mock my Function to return a custom property with Pester?

2.4k views Asked by At

I'm a bit new to PowerShell and specifically Pester testing. I can't seem to recreate a scenario for the function I am making Pester test.

Here is the code:

    $State = Get-Status

    if(State) {
    switch ($State.Progress) {
    0 {
       Write-Host "Session for $Name not initiated. Retrying."
      }

    100{
     Write-Host "Session for $Name at $($State.Progress) percent"
      }

    default {
     Write-Host "Session for $Name in progress (at $($State.Progress) 
    percent)."
       }
    }

I've mocked Get-Status to return true so that the code path would go inside the if block, but then the result doesn't have any value for $State.Progress.

My test would always go into the default block in terms of code path. I tried creating a custom object $State = [PSCustomObject]@{Progress = 0} to no avail.

Here is part of my Pester test:

    Context 'State Progress returns 0' {
    mock Get-Status {return $true} -Verifiable
    $State = [PSCustomObject]@{Progress = 0}
    $result =  Confirm-Session 
       it 'should be' {
           $result | should be "Session for $Name not initiated. Retrying."
        }
    }
1

There are 1 answers

1
Mark Wragg On BEST ANSWER

There are a couple of issues:

  • Per 4c's comments, your Mock may not be being called because of scoping (unless you have a describe block around your context not shown). If you change Context to Describe and then use Assert-VerifiableMocks you can see that the Mock does then get called.
  • You can't verify the output of code that uses Write-Host because this command doesn't write to the normal output stream (it writes to the host console). If you remove Write-Host so that the strings are returned to the standard output stream, the code works.
  • You can use [PSCustomObject]@{Progress = 0} to mock the output of a .Progress property as you suggested, but I believe this should be inside the Mock of Get-Status.

Here's a minimal/verifiable example that works:

$Name = 'SomeName'

#Had to define an empty function in order to be able to Mock it. You don't need to do this in your code as you have the real function.
Function Get-Status { }

#I assumed based on your code all of this code was wrapped as a Function called Confirm-Session
Function Confirm-Session  {
    $State = Get-Status

    if ($State) {
        switch ($State.Progress) {
        0 {
            "Session for $Name not initiated. Retrying."
          }

        100{
            "Session for $Name at $($State.Progress) percent"
          }

        default {
            "Session for $Name in progress (at $($State.Progress) percent)."
           }
        }
    }
}

#Pester tests for the above code:
Describe 'State Progress returns 0' {
    mock Get-Status {
        [PSCustomObject]@{Progress = 0}
    } -Verifiable

    #$State = [PSCustomObject]@{Progress = 0}

    $result = Confirm-Session

    it 'should be' {
        $result | should be "Session for $Name not initiated. Retrying."
    }

    it 'should call the verifiable mocks' {
        Assert-VerifiableMocks
    }
 }

Returns:

Describing State Progress returns 0
  [+] should be 77ms
  [+] should call the verifiable mocks 7ms