Author Posts

May 26, 2016 at 2:55 pm

I recently started trying to add full code coverage to some existing projects. In a few situations I'm not sure how to write tests or refactor to cover a few scenarios.

In one example from the DSC Repo, a resources uses a while loop to write-verbose errors in a catch block. Below is a rough example that is missing the same coverage areas.

I've tried using mocks to throw custom exceptions but so far have had little success. Any ideas?

File         Function  Line Command                                  
----         --------  ---- -------                                  
FindPath.ps1 Find-Path   28 $exception = $exception.innerException   
FindPath.ps1 Find-Path   29 Write-Verbose -Message $exception.message
function Find-Path
{
    [CmdletBinding()]
    Param
    (
        [String] $FilePath
    )

    try
    {
        if(Test-Path -Path $FilePath)
        {
            Write-Verbose 'File Exists'
            return 1
        }
        else
        {
            Write-Verbose 'File Doesnt Exist'
            return 2
        }
    }
    catch
    {
        $exception = $_
        Write-Verbose -Message $exception.message
        while ($null -ne $exception.innerException)
        {
            $exception = $exception.innerException
            Write-Verbose -Message $exception.message
        }

        return 3
    }
}
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"

Describe 'Find-Path' {
    Context 'should do something' {

        Mock Write-Verbose

        It 'should handle errors' {
            {Find-Path ''} | Should Not Throw
            Find-Path '' | Should Be 3
            Assert-MockCalled -commandName Write-Verbose
        }
        It 'should not exist' {
            {Find-Path '!'} | Should Not Throw
            Find-Path '!' | Should BeExactly 2
            Assert-MockCalled -commandName Write-Verbose
        }
        It 'should exist' {
            {Find-Path 'c:\Windows\regedit.exe'} | Should Not Throw
            Find-Path 'c:\Windows\regedit.exe' | Should Be 1
            Assert-MockCalled -commandName Write-Verbose
        }

    }
}

May 27, 2016 at 1:21 am

The code coverage report is telling you the tests are not executing the while logic in your catch block.
you need to examine the code and determine if it is needed and if so come up with a test to exercise it. I can not make your example use that code but I was able to use the following to exercise a similar code block

Function start-test {
[CmdletBinding()]
    Param(
        [String] $FilePath
    )
    while ($ex -eq $null){
        $ex = "TEST!!" 
        Write-Verbose -Message $ex
    }
}

Hope this helps

May 27, 2016 at 1:11 pm

Thanks for the input, but I don't think this is what I'm looking for. I could refactor the code a bit if it could simplify the tests. The main issue is getting coverage in the catch block.

The example in this post is something I just made for testing and to illustrate the problem.
The actual code I'm using looks more like the DSC hosts file.

May 28, 2016 at 2:49 am

Take a look at MSDN for for inner exception example as this looks like the catch is similar.

From the DSC hosts file Test-TargetResource has a try to catch exceptions from Test-HostEntry; which in turn calls Get-Content and Convert-EntryLine; which does some matches and returns an object. Nowhere down the tree is there a case for an exception that is thrown and caught outside of the Test-TargetResource function and then another exception thrown and caught by the Test-TargetResource function.

So the code coverage report is correctly telling you that there is unused code in the test cases. Based on the code being exercised by the tests it is either unneeded or you have more code to add (in the other functions) to be able to support the test.

May 28, 2016 at 11:48 pm

It appears that powershell does not keep the source for each throw so each throw in essence overwrites the exception record doping the inter exception info. you can cause this code to work with a mock that executes inline C# like the msdn example

function test-main{
    [CmdletBinding()]
    Param([String] $test)
    try{
        test-catchinner
    }catch{
        $exception = $_.exception
        Write-Verbose -Message $exception.Message
        #$exception.innerException|gm
        while ($null -ne $exception.innerException)
        {
            $exception = $exception.innerException
            Write-Verbose -Message $exception.Message
        }
    }
    try{
        [Example]::Main()
    }catch{
        $exception = $_.exception
        Write-Verbose -Message $exception.Message
        #$exception.innerException|gm
        while ($null -ne $exception.innerException)
        {
            $exception = $exception.innerException
            Write-Verbose -Message $exception.Message
        }
    }
}
Function test-catchinner{
    try{
        test-throwinner
    }catch{
        throw "Error in test-catchinner caused by test-throwinner"
    }
}
Function test-throwinner{
    throw "Error in test-throwinner"
}
$source = @'
using System;
public class AppException : Exception
{
   public AppException(String message) : base (message)
   {}

   public AppException(String message, Exception inner) : base(message,inner) {}
}

public class Example
{
   public static void Main()
   {
      Example ex = new Example();

      ex.CatchInner();

   }

   public void ThrowInner ()
   {
      throw new AppException("Exception in ThrowInner method.");
   }

   public void CatchInner() 
   {
      try {
         this.ThrowInner();
      }
      catch (AppException e) {
         throw new AppException("Error in CatchInner caused by calling the ThrowInner method.", e);
      }
   }
}
'@
Add-Type -TypeDefinition $source -Language CSharp 

test-main -Verbose

VERBOSE: Error in test-catchinner caused by test-throwinner
VERBOSE: Exception calling "Main" with "0" argument(s): "Error in CatchInner caused by calling the ThrowInner method."
VERBOSE: Error in CatchInner caused by calling the ThrowInner method.
VERBOSE: Exception in ThrowInner method.

The powershell functions only show that last throw while the C# code shows that whole chain via the innerexecption

Hope this helps