pester / Pester

Pester is the ubiquitous test and mock framework for PowerShell.
https://pester.dev/
Other
3.11k stars 473 forks source link

Feature request for 'Should' to output a boolean value #1712

Open DarkLite1 opened 4 years ago

DarkLite1 commented 4 years ago

1. General summary of the issue

Would it be possible for Should to produce a boolean value so it can be used within the script block of -ParameterFilter available on Mock? See the example code below to clarify.

Describe '-ParameterFilter test' {
    BeforeAll {
        Function Test-Colors {
            Param (
                [String[]]$Colors
            )
            $Colors.Contains('Green')
        }

        Mock Test-Colors

        Test-Colors -Colors @('Green', 'Red')
    }
    It "desired functionality" {
        Should -invoke Test-Colors -Scope Describe -ParameterFilter {
            $Colors | Should -Be @('Green', 'Red')
        }
    }
    It "workaround" {
        Should -invoke Test-Colors -Scope Describe -ParameterFilter {
            $Colors | Should -Be @('Green', 'Red')
            $true
        }
    }
}
  1. Possible solution

Maybe it would be great if a switch could be added like:

@('apple', 'kiwi') | Should -Be -OutBoolean @('apple', 'kiwi')
fflaten commented 4 years ago

What are you trying to solve? Why can't you use $Colors -contains 'Green' -and $Colors -contains 'Red'?

DarkLite1 commented 4 years ago

I could. there's nothing wrong wit that. But what if I wanted to check more than one item? Or to be exactly equal to another array? The test code would just become longer and longer or I would have to create my own helper function like AreArraysEqual as someone else did.

That's a bit redudant if Pester already has this capability with the should assertions. . Then simply why not allow them to be used within -ParameterFilter that only requires a boolean result?

fflaten commented 4 years ago

Bad example on my part, should've been $Colors[0] -eq 'Green' -and $Colors[1] -eq 'Red'.

Most should operators are just wrappers for the normal comparison operators with additional error reporting and Pester-logic in which case this wouldn't be necessary. In array-examples I see your point, but then making ArraysAreEqual would've been enough.

I'm not sure if functions/cmdlets are even supposed to work inside ParameterFilter even though some might work now. It feels a bit too complex. Will have to wait for @nohwnd. 🙂

nohwnd commented 4 years ago

I think we can wrap the parameter filter evaluation in try catch, and consider the filter to be failed if there is an exception. Otherwise I am not sure how we would handle multiple Shoulds in the same filter, because they would just return $true / $false so all of them would run, and then we would check if there was at least some failure, but it would be very hard to see which one. Instead we can catch the error and write it to the Diagnostic log, when enabled.

DarkLite1 commented 4 years ago

Yeah, I've noticed when combining the should test with normal x -eq x the testresult becomes unreliable.

Depending on if the line $Colors[0] -eq 'wrong' is before or after the should it might fail or succeed.

        Should -invoke Test-Colors -Scope Describe -ParameterFilter {
            $Colors[0] -eq 'wrong'
            $Colors | Should -Be @('Green', 'Red')
            $true
        }
johlju commented 2 years ago

I like this idea, today for it to handle -Times <number> correctly when using a parameter filter one must do something this:

Should -Invoke -CommandName Write-Verbose -ParameterFilter {
    # Only test the command that output the string that should be tested.
    $correctMessage = $Message -match 'My Message'

    # Only test string if it is the correct verbose command
    if ($correctMessage)
    {
        $Message | Should -MatchExactly $RegEx1
        $Message | Should -MatchExactly $RegEx2
    }

    # Return wether the correct command was called or not.
    $correctMessage
} -Exactly -Times 1 -Scope It

In the above snippet it would not help much to have OutBoolean (or PassThru) because as @nohwnd said the first Should could return $true while the other could return $false and it shuld only be evaulated IF it is the correct call of (in this case) Write-Verbose.

Would it work if there were no value of $true returned it is always assumed that it is $false, and always throw on a Should that fails evaluation. But in the above snippet it would not help as much as the below would not be really intuitive (as the above is):

Should -Invoke -CommandName Write-Verbose -ParameterFilter {
    # Only test the command that output the string that should be tested.
    $correctMessage = $Message -match 'My Message'

    # Only test string if it is the correct verbose command
    if ($correctMessage)
    {
        $Message | Should -MatchExactly $RegEx1 -PassThru # Or -OutBoolean
        $Message | Should -MatchExactly $RegEx2 -PassThru # Or -OutBoolean
    }
} -Exactly -Times 1 -Scope It

There would only be sometimes the OutBoolean (or PassThru) could be useful.