Can Pester mock an exception?
Asked Answered
I

2

10

I'm working on some Pester test cases and I'm looking at the CodeCoverage results. In most sets of test cases where we the code contains a try/catch we get 0% coverage on the catch. Here's an example:

function Test-Is64Bit()
{

    $Result = $false

    try
    {
        if ((Get-WmiObject -Class "Win32_OperatingSystem").OSArchitecture -eq '64-bit')
        {
            $Result = $true
        }
    }
    catch
    {
        Write-Error $_.Exception | Format-List -Force
    }

    return $Result
}

It's easy enough to mock the Get-WmiObject return value to test the $true condition.

I've already tried a number of ideas to mock an exception from Get-WmiObject but in every case the exception is passed through to the console instead of being caught by Pester and passing the test. Below is the best I've come up with but it still doesn't work.

Context "Unit tests for Get-WmiObject exception" {
    # mock the Get-WmiObject cmdlet for unit testing
    Mock Get-WmiObject {
        Throw
    } -ParameterFilter { $Class -And $Class -eq 'Win32_OperatingSystem' }

    It "Function test passes" {
        { Test-Is64Bit } | Should Be $false
        Assert-MockCalled Get-WmiObject -Scope It -Exactly -Times 1
    }
}

This test results in:

     Context Unit tests for Get-WmiObject error
      [-] Function test passes 138ms
        Expected: {False}
        But was:  { Test-Is64Bit }
        at line: 62 in .\Tests\Test-Is64Bit.Tests.ps1
        62:                             { Test-Is64Bit } | Should Be $false
Tests completed in 590ms
Tests Passed: 4 Failed: 1 Skipped: 0 Pending: 0 Inconclusive: 0

Code coverage report:
Covered 80.00 % of 10 analyzed Commands in 1 File.
Missed commands:

File             Function     Line Command
----             --------     ---- -------
Test-Is64Bit.ps1 Test-Is64Bit   38 Write-Error $_.Exception
Test-Is64Bit.ps1 Test-Is64Bit   38 Format-List -Force

Is the any way to mock an exception being thrown by Get-WmiObject so we can have Pester fall into the catch and finally achieve 100% code coverage?

My current test code looking for exceptions

Context "Unit tests for Get-WmiObject exception" {
    # mock the Get-WmiObject cmdlet for unit testing
    Mock Get-WmiObject {
        Throw 'Some Error'
    } -ParameterFilter { $Class -And $Class -eq 'Win32_OperatingSystem' }

    It 'Get-WmiObject should throw' {
        { Get-WmiObject -Class 'Win32_OperatingSystem' } | Should Throw 'Some Error'
    }

    It "Throws exception" {
        { Test-Is64Bit } | Should Throw 'Some Error'
        Assert-MockCalled Get-WmiObject -Scope It -Exactly -Times 1
    }
}

The above code results in this:

     Context Unit tests for Get-WmiObject exception
       [+] Get-WmiObject should throw 89ms
 Test-Is64Bit : System.Management.Automation.RuntimeException: Some Error At
 C:\ONESolutionFinance\Main\ReleaseManagement\PowerShell-Module\SPSNoDeps\Tests\Test-Is64Bit.Tests.ps1:66
 char:7
 +                 { Test-Is64Bit } | Should Throw 'Some Error'
 +                   ~~~~~~~~~~~~
     + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
     + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-Is64Bit

       [-] Throws exception 51ms
         Expected: the expression to throw an exception with message {Some Error}, an exception was not raised, message was {}
             from line:2 char:5
             +                 Throw 'Some Error'
             +                 ~~~~~~~~~~~~~~~~~~
         at line: 66 in C:\ONESolutionFinance\Main\ReleaseManagement\PowerShell-Module\SPSNoDeps\Tests\Test-Is64Bit.Tests.ps1
         66:                             { Test-Is64Bit } | Should Throw 'Some Error'

Testing for $false returns this:

    Context Unit tests for Get-WmiObject exception
      [+] Get-WmiObject should throw 162ms
Test-Is64Bit : System.Management.Automation.RuntimeException: Some Error
At C:\ONESolutionFinance\Main\ReleaseManagement\PowerShell-Module\SPSNoDeps\Tests\Test-Is64Bit.Tests.ps1:66 char:5
+                 Test-Is64Bit | Should Be $false
+                 ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-Is64Bit

      [+] Throws exception 92ms
Ileenileitis answered 8/8, 2017 at 16:34 Comment(0)
S
11

Yes it can! Here's an example:

Describe 'Mock an exception' {
    Mock Get-WMIObject { Throw 'some error' }

    It 'Get-WMIObject should throw' {
        {Get-WMIObject -class someclass} | Should Throw 'some error'
    }
}

I think your code isn't working because:

{ Test-Is64Bit } | Should Be $false

Should be:

 { Test-Is64Bit } | Should Throw

You use a ScriptBlock { } before a Should when you're using Should Throw.

Strenuous answered 8/8, 2017 at 17:13 Comment(7)
I've updated my original post to make the question a bit more clear. Mocking a Get-WmiObject exception and testing for the exception in Pester works just fine. But is there a way to test the Test-Is64Bit function and have Get-WmiObject throw an exception such that the Pester test falls into the catch inside Test-Is64Bit. I still get failed tests and see exceptions thrown to the console.Ileenileitis
Are you using -erroraction stop on get-wmiobject in your function? If not maybe your catch never gets invoked?Strenuous
I've tried both with and without a -ErrorAction stop on Get-WmiObject. The same exception is shown in the console.Ileenileitis
Are you still testing for a $false result, rather than a throw?Strenuous
Or if your try catch means that it doesn't throw an exception but instead returns false, you need your test to then be this: Test-Is64Bit | Should Be $falseStrenuous
I updated my original post with the actual Pester test code and the results of running it.Ileenileitis
Try changing ` $Class -And $Class -eq 'Win32_OperatingSystem'` to $Class -eq 'Win32_OperatingSystem'. The former seems unnecessary and potentially stopping the Mock from being invoked. I assume the Assert-MockCalled is showing that also right? It's not in your output above.Strenuous
O
0
function Test-LocalFile
{
    param (
        [string]
        $filepath
    )
    try {
        $FileInfo = get-item -Path $filepath -ErrorAction SilentlyContinue
        if ($FileInfo.getType().Name -eq "FileInfo") {
            return $true
        }
    } catch {

         Write-Error -Message "$($_.Exception.Message)"
         Throw
    }
}

Here is the total solution for the upper defined function how to handle the exception

Describe "Unit testing for exception handling" {
    Context "unit test showing exception"{

    #arrange
    Mock Write-Error{}
    Mock get-item  {
           Throw 'some error'
    }

    #act
    Write-Error 'some error'
    #assert
    It "test-Local FileThrows exception" {
             {Test-LocalFile -filepath test.txt}  | Should Throw 'some error'
            Assert-MockCalled get-item -Scope It -Exactly -Times 1
        }
    }
}
Ology answered 22/4, 2020 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.