Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pester for Non-Terminating Error #366

Closed
dotps1 opened this issue Jun 12, 2015 · 11 comments
Closed

Pester for Non-Terminating Error #366

dotps1 opened this issue Jun 12, 2015 · 11 comments

Comments

@dotps1
Copy link

dotps1 commented Jun 12, 2015

I reviewed the Should Wiki Page, and i apologize if i missed how to use this, but i was working with some error handling, and i want to Pester for non terminating errors as well as Exceptions. Basically, i have some tests in a function that are in the Process block, and i still want the End block to process, so in this case i want to use Write-Error rather then Throw, so, in my pester assertion, i want to make sure the Catch block is being hit, and the Write-Error is being executed, and im not sure how to do that.

It 'Error should be raised' {
     My-FunctionThatWritesErrorForInvalidPath -Path '.\InvalidPath' | Should #Here is where im lost, maybe Should Error would be nice.
}

I hope that makes sense and im sorry if im just using Write-Error incorrectly, and i could use the 'Should Throw' assertion.

thanks for any help.

@ChaseFlorell
Copy link

My-FunctionThatWritesErrorForInvalidPath -Path '.\InvalidPath' | Should Throw

@dlwyatt
Copy link
Member

dlwyatt commented Jun 12, 2015

Should Throw only works for terminating errors, so you can do one of two things. You can either use -ErrorAction Stop to force your non-terminating errors to become terminating (though this would prevent your End block from running), or you can use something like -ErrorVariable and then do assertions against that. Something along these lines:

It 'Error should be raised' {
     My-FunctionThatWritesErrorForInvalidPath -Path '.\InvalidPath' -ErrorVariable err
     $err.Count | Should Not Be 0
     $err[0].Exception.Message | Should Be "Path '.\InvalidPath' is not valid" # or something like that
}

# or

It 'Error should be raised' {
     $scriptBlock = { My-FunctionThatWritesErrorForInvalidPath -Path '.\InvalidPath' -ErrorAction Stop }
     $scriptBlock | Should Throw "Path '.\InvalidPath' is not valid" # or something like that
}

@dotps1
Copy link
Author

dotps1 commented Jun 12, 2015

thanks Dave, that works fine for me! keep up the good work! feel free to close this thread if you wish.

@ChaseFlorell
Copy link

Ah, sorry, I didn't read the question all the way through. I personally always throw and then run cleanup code in finally. I see you're using a different (and valid) approach.

@theficus
Copy link

I work around this with two helper functions that I call in all my tests:

  • Method 1: Expect script to fail (in other words, throw a terminating exception):
Function InvokeShouldFail
{
    Param([Parameter(Mandatory)][string]$script)
    Write-Host Invoking command and expecting failure: $script
    $didFail = $false
    try
    {
        iex $script
        Write-Warning Did not fail: $script
    }
    catch
    {
        Write-Host "Script failed as expected: $_"
        $didFail = $true
    }

    $didFail | Should Be $true
}
  • Method 2: Expect an error (in other words, it should fail, but not in a terminating way). You could always extend this to look for specific error text as well if you want to assert not only that it errors, but it errors with a specific string but I haven't had a need for this yet.
Function InvokeShouldError {
    Param([Parameter(Mandatory)][string]$script)
    Write-Host Invoking command and expecting error: $script
    $didError = ""
    iex $script -ErrorVariable didError

    Write-Host Error result: $didError
    $didError | Should Not BeNullOrEmpty
}

Then in my unit test, I call it something like this

It "Does something" {
    # This should fail, but not with a thrown exception
    InvokeShouldError { Remove-Something -Name NotReallyExist }
}

@dlwyatt dlwyatt closed this as completed Jul 13, 2015
@midacts
Copy link

midacts commented Jul 17, 2017

I ran into this as well.
Thanks for the help.

@fgimian
Copy link

fgimian commented May 5, 2019

Should Throw only works for terminating errors, so you can do one of two things. You can either use -ErrorAction Stop to force your non-terminating errors to become terminating (though this would prevent your End block from running), or you can use something like -ErrorVariable and then do assertions against that. Something along these lines:

It 'Error should be raised' {
     My-FunctionThatWritesErrorForInvalidPath -Path '.\InvalidPath' -ErrorVariable err
     $err.Count | Should Not Be 0
     $err[0].Exception.Message | Should Be "Path '.\InvalidPath' is not valid" # or something like that
}

# or

It 'Error should be raised' {
     $scriptBlock = { My-FunctionThatWritesErrorForInvalidPath -Path '.\InvalidPath' -ErrorAction Stop }
     $scriptBlock | Should Throw "Path '.\InvalidPath' is not valid" # or something like that
}

Thanks so much for these tips, very helpful.

Just one little note. The first solution will still display the error on the screen which may be rather distracting while running Pester. You may simply add -ErrorAction SilentlyContinue to avoid this happening.

I like the first approach as it also allows you to validate return values.

e.g.

Describe 'Get-RelativePath' {
    Context 'Filesystem Paths' {
        It 'Should return a relative path when given absolute paths' {
            $relativePath = Get-RelativePath `
                -Path 'C:\wow\files\my app' `
                -BasePath 'HKLM:\SOFTWARE\my app' `
                -ErrorVariable errors `
                -ErrorAction SilentlyContinue

            $errors.Count | Should Be 1
            $errors[0].Exception.Message | Should Be (
                'The scheme of the path and base path provided do not match.'
            )

            $relativePath | Should Be $null
        }
    }
...

Cheers
Fotis

@deadlydog
Copy link

I believe this is probably a pretty common scenario, so I updated the Should documentation to include a blurb about it in the Throw example, with a link back to this issue 🙂

@RobJohnson459
Copy link

I know I'm new and this may be obvious to those who have used this a lot before, but doesn't the stop keyword in the code blocks above need to be in quotes?

In any case, I ran into a situation where I wanted to test that a warning was printing, so I had to make use of the piping operator to test:

Describe 'Get-RelativePath' {
    Context 'Filesystem Paths' {
        It 'Should return a warning when given incorrect paths' {
            $temporaryFile - $PSScriptRoot + '\notARealFile.txt'
            New-Item -Path $temporaryFile -ItemType 'file' -Force
            Get-RelativePath -Path 'C:\invalid\path'  3> $temporaryFile
            $temporaryFile | Should -not -BeNullOrEmpty
            $temporaryFile | Should Be ( 'your Error Message here' )
        }
    }

All this does is pipe all the errors to a file named 'notARealFile.txt' that you can then check yourself.

@fflaten
Copy link
Collaborator

fflaten commented Jun 4, 2021

doesn't the stop keyword in the code blocks above need to be in quotes?

No. -ErrorAction is of type enum (a pre-defined list of values) and not a string. Even if it was a string parameter, values without whitespace are usually okay (fixed automatically by powershell).

All this does is pipe all the errors to a file named 'notARealFile.txt' that you can then check yourself.

You are actually saving warnings (stream 3), not errors. So this is unrelated to the issue. Tip: If this command is a "advanced function" (ex specifies [CmdLetBinding()]), then you can use Get-RelativePath -Path 'C:\invalid\path' -WarningVariable mywarnings to catch the warnings. If not and if the command shouldn't return any other output in this test that you need, then you can redirect warnings to standard output and store in a variable like normal results, ex $myWarnings = Get-RelativePath -Path 'C:\invalid\path' 3>&1

@RobJohnson459
Copy link

Thanks for the -ErrorAction clarification. And piping to a variable would be a better idea for my use case, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants