Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Pester mock an exception?

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
like image 686
Dave Wiard Avatar asked Aug 08 '17 16:08

Dave Wiard


Video Answer


1 Answers

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.

like image 56
Mark Wragg Avatar answered Nov 03 '22 10:11

Mark Wragg