Как правильно сравнивать хеш-таблицы в тестах Pester?
то есть следующее дает ошибку: InvalidResult: Expected System.Collections.Hashtable, but got System.Collections.Hashtable.
$expected = @{one=1;two=2}
$actual = @{one=1;two=2}
$actual | Should -Be $expected
Если я сначала приведу эти HastTables к PSCustomObjects, я получу InvalidResult: Expected @{one=1; two=2}, but got @{one=1; two=2}.
$expected = [PSCustomObject]@{one=1;two=2}
$actual = [PSCustomObject]@{one=1;two=2}
$actual | Should -Be $expected
Аналогично, если мы сравним массивы (KeyCollection) ключей, мы получим InvalidResult: Expected @('one', 'two'), but got @('one', 'two').
$expected = @{one=1;two=2}
$actual = @{one=1;two=2}
$actual.Keys | Should -Be $expected.Keys
Я, конечно, могу написать свой собственный метод Test-HashTablesAreEqual, чтобы убедиться, что массив ключей точно совпадает, а затем проверить значение каждого ключа; но тогда мне придется либо создать собственное исключение, либо просто вернуть значение true/false и использовать его в Pester; который не дает значимого сообщения об ошибке (т. е. я бы хотел Expected @{one=1;two=2}, but got @{one=1;two=4} или что-то подобное).
Я могу разбить все на набор более простых сравнений, что дает немного лучший результат, но здесь я все еще не могу легко увидеть, какой ключ дает неправильное значение, если есть разница:
([string[]]$actual.Keys) | Should -Be ([string[]]$expected.Keys)
foreach ($key in $actual.Keys){$actual[$key] | Should -Be $expected[$key]}
Примечание: я могу использовать параметр -Because, чтобы добавить некоторый контекст к приведенному выше; например указывая, что я сравниваю ключи хеш-таблицы, или указывая, какое значение ключа я сравниваю... Все еще не идеально, но это делает его более разумным.
В качестве альтернативы я могу немного обойти это, преобразовав его в Json; но это похоже на хак; и хотя это сработало для моих базовых тестов, я подозреваю, что нет никакой гарантии относительно порядка, в котором будут появляться ключи (особенно, если фактические ключи имеют какие-либо хеш-коллизии при построении таблицы, из-за чего их порядок становится менее предсказуемым).
($actual | ConvertTo-Json) | Should -Be ($expected | ConvertTo-Json)
Существует ли «правильный» способ выполнить это сравнение, который выдает полезное сообщение об ошибке, если ожидаемые и фактические значения не совпадают?
Это решение, которое я придумал в качестве оболочки для некоторых из вышеперечисленных идей... Эта пользовательская функция напрямую вызывает команды Pester, поэтому мы по-прежнему получаем ожидаемые выходные данные Pester, а не полагаемся на то, что функция вернет что-то для Pester для последующей обработки. . Модуль Assert, упомянутый Марком Рэггом, кажется лучшим вариантом, если он совместим с PS Core; но в то же время, если мне все равно придется использовать мою собственную логику (например, для пользовательского утверждения), это будет похоже на быструю победу:
function Compare-HashtablesInPester {
[CmdletBinding(DefaultParameterSetName='AsHashtable')]
Param (
[Parameter(Mandatory)]
[AllowNull()]
[Hashtable]$Expected
,
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName='AsHashtable')]
[AllowNull()]
[Hashtable]$Actual
,
[Parameter(Mandatory, ParameterSetName='AsPSCustomObject')]
[AllowNull()]
[PSCustomObject]$ActualObject
,
[Parameter(ParameterSetName='AsPSCustomObject')]
[Int32]$Depth = 20 # default for ConvertTo-Json is 2, default for ConvertFrom-Json is 1024... 20 seems a reasonable default for the 2
)
if (($null -eq $Expected) -or ($Expected.Count -eq 0)) {
if ($PSCmdlet.ParameterSetName -eq 'AsHashtable') {
$Actual | Should -BeNullOrEmpty
} else {
$ActualObject | Should -BeNullOrEmpty
}
} else {
if ($PSCmdlet.ParameterSetName -eq 'AsPSCustomObject') {
$Actual = $ActualObject | ConvertTo-Json -Depth $Depth | ConvertFrom-Json -Depth $Depth -AsHashTable
}
$Actual | Should -HaveCount $Expected.Count
[object[]]$actualKeys = $Actual.Keys | Sort-Object
[object[]]$expectedKeys = $Expected.Keys | Sort-Object
$actualKeys | Should -BeExactly $expectedKeys -Because 'these are the given hashtable''s keys'
foreach ($key in $Actual.Keys) {
$Actual[$key] | Should -BeExactly $Expected[$key] -Because "this is the value of the [$key] key in the given hashtable"
}
}
}





Я не думаю, что есть простой способ сделать это. Преобразование в JSON не является плохим вариантом, особенно если вы можете сначала принудительно [упорядочить] хеш-таблицу.
У Якуба Яреша, который поддерживает Pester, есть дополнительный модуль под названием Assert, который реализует некоторые более сложные/настраиваемые утверждения для использования в Pester. Вы можете найти это здесь:
У него есть Assert-Equivalent, который можно использовать для сравнения объектов. Например:
$expected = @{one=1;two=2}
$actual = @{one=1;two=3}
Assert-Equivalent -Actual $actual -Expected $expected
возвращает:
Expected and actual are not equivalent!
Expected:
@{one=1; two=2}
Actual:
@{one=1; two=3}
Summary:
Expected hashtable '@{one=1; two=2}', but got '@{one=1; two=3}'.
Expected property .two with value '2' to be equivalent to the actual value, but got '3'.
At C:\Users\wragg\OneDrive\Documents\WindowsPowerShell\Modules\Assert\0.9.5\src\Equivalence\Assert-Equivalent.ps1:674 char:9
+ throw [Assertions.AssertionException]$message
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], AssertionException
+ FullyQualifiedErrorId : Expected and actual are not equivalent!
Expected:
@{one=1; two=2}
Actual:
@{one=1; two=3}
Summary:
Expected hashtable '@{one=1; two=2}', but got '@{one=1; two=3}'.
Expected property .two with value '2' to be equivalent to the actual value, but got '3'.
Однако модуль в последнее время не обновлялся. Кажется, у меня это работает нормально в Windows PowerShell, но не в PowerShell 7.4, где я получаю
Unable to find type [Assertions.AssertionException].
Когда объекты не совпадают.
Возможно, вам будет полезно взглянуть на его реализацию, если вы захотите создать свою собственную:
Pester поддерживает пользовательские утверждения, как описано здесь:
Я пошел по пути обычного оператора -Should. По сути, я сравниваю представления операндов в формате JSON. Если они разные, я вывожу унифицированные различия, используя git diff --no-index $originalJsonPath $newJsonPath, что очень помогает различить различия объектов со многими свойствами.
Пару недель назад Assert получил новый выпуск с исправлением поддержки PSv7 AFAIK. Также предупреждаем, что Assert будет объединен с Pester v6. :)
Отлично, спасибо... модуль
Assertвыглядит идеально; хотя у меня также возникают проблемы с импортом его в PS Core.. Буду следить за ним на случай, если будет больше активности (я вижу, что за последний месяц были коммиты, так что он не совсем бездействующий).