Repository navigation
-HaveCount assertion operator does not work when using strong typing on $null #1000
Description
1. Provide a general summary of the issue in the Title above
Pester 4.2.0 introduced a new -HasCount assertion operator. When migrating existing tests, by replacing $variable.Count | Should -Be $expectedCount to $variable | Should -HaveCount $expectedCount, I found edge cases where this does not work. I created Pester tests to demonstrate this behaviour in a simple case but later on upon request I can also give 2 more comples examples where -HasCount does not work. The idea is to first investigate/fix the first simple problem and only afterwards approach the more complex test cases.
Describe "Pesters -HaveCount operator should not fail on `$null object that has Count property" {
It "when not using strong typing" {
$aliases = Get-Alias | Select-Object -First 0
$aliases.Count | Should -Be 0
$aliases | Should -HaveCount 0
}
It "when LHS is casted to [System.Array]" {
[System.Array] $aliases = Get-Alias | Select-Object -First 0
$aliases.Count | Should -Be 0
$aliases | Should -HaveCount 0
}
It "when RHS is casted to [System.Array]" {
$aliases = [System.Array] (Get-Alias | Select-Object -First 0)
$aliases.Count | Should -Be 0
$aliases | Should -HaveCount 0
}
It "when LHS and RHS are casted to [System.Array]" {
[System.Array] $aliases = [System.Array] (Get-Alias | Select-Object -First 0)
$aliases.Count | Should -Be 0
$aliases | Should -HaveCount 0
}
}2. Describe Your Environment
Pester version : 4.3.1
PowerShell version : 5.1.16299.248
PowerShell version : 6.0.1
PowerShell version : 6.1.0-preview.589
OS version : Microsoft Windows NT 10.0.16299.0
3. Expected Behavior
All 4 tests above should pass
4.Current Behavior
The first test passed but not the other 3. By debugging it, the objects seem to be of the same type ($null), therefore I cannot explain why Pester behaves differently. PowerShell has .Count property on $null by design.
Describing Pesters -HaveCount operator should not fail on $null object that has Count property
[+] when not using strong typing 387ms
[-] when LHS is casted to [System.Array] 132ms
Expected an empty collection, but got collection with size 1 @().
11: $aliases | Should -HaveCount 0
at <ScriptBlock>, C:\Users\cberg\git\bla.tests.ps1: line 11
[-] when RHS is casted to [System.Array] 105ms
Expected an empty collection, but got collection with size 1 @().
17: $aliases | Should -HaveCount 0
at <ScriptBlock>, C:\Users\cberg\git\bla.tests.ps1: line 17
[-] when LHS and RHS are casted to [System.Array] 37ms
Expected an empty collection, but got collection with size 1 @().
23: $aliases | Should -HaveCount 0
at <ScriptBlock>, C:\Users\cberg\git\bla.tests.ps1: line 23
Tests completed in 662ms
Tests Passed: 1, Failed: 3, Skipped: 0, Pending: 0, Inconclusive: 0
5. Possible Solution
Document more clearly on what types the new HasCount assertion operator works but to me it rather looks like a Pester internal bug that needs to be resolved.
6. Context
As described in paragraph: migrating to new -HasCount operator, i.e. not a blocking issue.
Activity
markekraus commentedon Feb 27, 2018
similarly.. I would expect this to work on HashTable and Dictionaries to could the number of Items in the dictionary.
Result:
bergmeister commentedon Feb 27, 2018
Yes, I have seen similar issues with different .net collection types as well but decided to rather raise this simple one first.
JimMoyle commentedon Aug 7, 2018
As the count property on objects returns non intuitive results as described above, I'd change $ActualValue.Count to ($ActualValue | Measure-Object).count This should fix the issues above.
JimMoyle commentedon Aug 7, 2018
Although a different solution would need to be included for PoSh 2.0 as measure-object was introduced in 3.0
fflaten commentedon May 24, 2021
The original issue is mostly a PowerShell-issue of iterating $null through pipeline. MS fixed the same issue with
foreach ($a in $null) { # ran once pre-v3 }-loop in PSv3, but this one is still there:Now that PowerShell v2 supports is dropped, the suggested fix with
Measure-Objectcan be considered as it will fix these edge-cases. Doesn't look like it affects performance that much:As for the other issues mentioned here:
...but got collection with size 1 @($null)..-HaveCountwith hashtable/dictionaries are covered inShould -Bedoesn't enumerate some collections? #1200 and Problem using HaveCount on [hashtable] #1234.nohwnd commentedon May 25, 2021
10 times slower seems like a lot. But it is a start.
fflaten commentedon May 25, 2021
It was even worse in my Unix container tbh, but with real life scenarios (fewer calls? Smaller collections?) it might not affect the total that much.
We can probably scratch this approach as I just noticed it's a breaking change. It doesn't just ignore these edge cases, it drops null completely.
Measure-Objectwill break@(1,2,$null,4) | Should -HaveCount 4I think the biggest issue here is the lack of null in the formatted error-message (Update: This is fixed in 5.3.0-alpha2)
nohwnd commentedon Jun 25, 2026
This comes down to what
[System.Array]does to an empty pipeline.[System.Array] (Get-Alias | Select-Object -First 0)doesn't give you$nullor@(), it gives you@($null)- a one-element array holding$null:Should -HaveCountuses-SupportsArrayInput, so it collects the pipeline and sees one item ($null), hence size 1. The LHS-cast case passes because there the variable just stays$null.So the open question is a behavior one: should
<a single $null> | Should -HaveCount 0pass? We'd have to treat a lone piped$nullas empty, which then makes$null | Should -HaveCount 1fail - surprising the other way. I'd rather decide that explicitly before changing-HaveCount. What do you think it should do?Count dictionary entries in Should -HaveCount (pester#2756)