Pester icon indicating copy to clipboard operation
Pester copied to clipboard

Problem using HaveCount on [hashtable]

Open johlju opened this issue 5 years ago • 7 comments

1. General summary of the issue

When using HaveCount on a hashtable, the count is always 1. The following snippet shows the problem (both in Windows PowerShell and PowerShell Core).

Describe 'Get-HashTable' {
    Context 'When returning a hashtable' {
        It 'Test' {
            $getHashTableResult = @{
                Property1 = '1'
                Property2 = '3'
            }

            $getHashTableResult | Should -BeOfType [System.Collections.Hashtable]
            $getHashTableResult | Should -HaveCount 2
        }
    }
}

2. Describe Your Environment

Pester version     : 5.1.1 C:\source\SqlServerDsc\output\RequiredModules\Pester\5.1.1\Pester.psm1
PowerShell version : 7.1.0
OS version         : Microsoft Windows NT 10.0.19042.0

3. Expected Behavior

HaveCount should report the correct number of properties in the hashtable.

4.Current Behavior

Fails with an error even though there are two properties in the hashtable.

Starting discovery in 1 files.
Discovery finished in 22ms.
[-] Get-HashTable.When returning a hashtable.Test 8ms (7ms|1ms)
 Expected a collection with size 2, but got collection with size 1 @(System.Collections.Hashtable).
 at $getHashTableResult | Should -HaveCount 2, C:\Users\johan.ljunggren\Desktop\Bug_HaveCount.ps1:10

5. Possible Solution

NOTE: The below screenshots are from Pester 4.x, but the same issue still exist in 5.1.1.

The problem seems that it compares the wrong array. Instead of checking $ActualValue.Count, it should check $ActualValue[0].Count (at least in this case).

image

 [DBG]> $ActualValue.Count
1
 [DBG]> $ActualValue[0].Count
2

image

6. Context

Very minor inconvenience. Having HaveCount would just make the code prettier. Workaround is using the following instead.

$getHashTableResult.Count | Should -Be 2

johlju avatar Feb 02 '19 14:02 johlju

I have updated the issue for the Pester 5.1.1 where the issue still exist. It is also not possible to use ActualValue.

Describe 'Get-HashTable' {
    Context 'When returning a hashtable' {
        It 'Test' {
            $getHashTableResult = @{
                Property1 = '1'
                Property2 = '3'
            }

            Should -ActualValue $getHashTableResult -HaveCount 2
        }
    }
}

johlju avatar Dec 19 '20 10:12 johlju

It is not possible to use $getHashTableResult.Keys either.

Describe 'Get-HashTable' {
    Context 'When returning a hashtable' {
        It 'Test' {
            $getHashTableResult = @{
                Property1 = '1'
                Property2 = '3'
            }

            Should -ActualValue $getHashTableResult.Keys -HaveCount 2
        }
    }
}
Starting discovery in 1 files.
Discovery finished in 18ms.
[-] Get-HashTable.When returning a hashtable.Test 7ms (6ms|1ms)
 Expected a collection with size 2, but got collection with size 1 @(@('Property1', 'Property2')).
 at Should -ActualValue $getHashTableResult.Keys -HaveCount 2, C:\Users\johan.ljunggren\Desktop\Bug_HaveCount.ps1:9

johlju avatar Dec 19 '20 10:12 johlju

You can use $getHashTableResult.Keys | Should -HaveCount 2 or $getHashTableResult.GetEnumerator() | Should -HaveCount 2.

A hashtable is considered a single item when passed through the pipeline (and in a foreach-loop), so you would need $getHashTableResult.GetEnumerator() to make it work. Keys however can be looped through directly. We could add logic to work around it for dictionaries, but personally I'd recommend not to do that considering this is normal powershell behaviour and not a Pester-specific quirk. So it might be best to let Pester behave the same as a normal foreach-loop or Foreach-Object will.

Should -ActualValue $getHashTableResult.Keys -HaveCount 2 doesn't currently work as Should doesn't loop $ActualValue when using the parameter directly, only when provided through the pipeline. That's normally solved by adding a foreach($i in $ActualValue)-loop in Should's process block, but it will break nested array sent through pipeline ++, so this limitation is probably by-design. @nohwnd ?

fflaten avatar Dec 20 '20 16:12 fflaten

I good with closing this issue. If Should -ActualValue $getHashTableResult.Keys -HaveCount 2 should work then we can open up a separate issue for that. Let see what @nohwnd thinks.

johlju avatar Dec 20 '20 16:12 johlju

Stumbled upon this issue with Pester 5.1.1:

Describe 'a hash table should be correctly counted with' {
    BeforeAll {
        $hashTable = @{
            Key1 = 1
            Key2 = 2
        }
    }
    It '-BeExactly' {
        $hashTable.Count | Should -BeExactly 2
    }
    It '-HaveCount' {
        $hashTable | Should -HaveCount 2 # FAIL
    }
}
Describe 'an array should be correctly counted with' {
    BeforeAll {
        $array = @(1, 3)
    }
    It '-BeExactly' {
        $array.Count | Should -BeExactly 2
    }
    It '-HaveCount' {
        $array | Should -HaveCount 2
    }
}

It would be great if Pester would simply check the type and if it's a hasthtable check how many keys there are. @nohwnd , is this something that would be considered?

DarkLite1 avatar Jan 20 '21 12:01 DarkLite1

Having also stumbled over this recently too, I have to agree.

From a usability point of view, it's very confusing: You're testing an object that has a Count property, but Should -HaveCount doesn't work on it...?!

iambic69 avatar Apr 22 '21 18:04 iambic69

@iambic69 No disagreement here, but sometimes there are technical limitations. The problem is that due to the pipeline-behavior in PowerShell which doesn't enumerate hashtables-entries unlike other ICollection/IEnumerable-types, then by fixing @{"one"=1,"two"=2} | Should -HaveCount 2 we would just as easily break arrays with a single dictionary-item

# Would fail due to count of 2 keys and not 1 hashtable as expected
@(@{"one"=1,"two"=2}) | Should -HaveCount 1

That is unless someone has an idea of how to properly detect "a single dictionary" vs "array with single dictionary" through the pipeline.

fflaten avatar Apr 23 '21 16:04 fflaten