问题描述
将 Hashtable
用作 Should
的输入时,Pester仅输出类型名称,而不输出内容:
When a Hashtable
is used as input for Should
, Pester outputs only the typename instead of the content:
Describe 'test' {
It 'test case' {
$ht = @{ foo = 21; bar = 42 }
$ht | Should -BeNullOrEmpty
}
}
输出:
Expected $null or empty, but got @(System.Collections.Hashtable).
预期的输出,例如:
Expected $null or empty, but got @{ foo = 21; bar = 42 }.
原因
查看 Pester来源输入由私有函数 Format-Nicely
格式化,如果值类型为 Hashtable
,该函数将强制转换为 String
.归结为调用 Hashtable :: ToString()
,后者仅输出类型名称.
Cause
Looking at Pester source, the test input is formatted by private function Format-Nicely
, which just casts to String
if the value type is Hashtable
. This boils down to calling Hashtable::ToString()
, which just outputs the typename.
作为一种解决方法,我目前正在从 Hashtable
派生一个重写 ToString
方法的类.在将输入传递给 Should
之前,我将其转换为该自定义类.这使Pester在格式化测试结果时调用了我覆盖的 ToString
方法.
As a workaround I'm currently deriving a class from Hashtable
that overrides the ToString
method. Before passing the input to Should
, I cast it to this custom class. This makes Pester call my overridden ToString
method when formatting the test result.
BeforeAll {
class MyHashTable : Hashtable {
MyHashTable( $obj ) : base( $obj ) {}
[string] ToString() { return $this | ConvertTo-Json }
}
}
Describe 'test' {
It 'test case' {
$ht = @{ foo = 21; bar = 42 }
[MyHashTable] $ht | Should -BeNullOrEmpty
}
}
现在Pester以JSON格式输出 Hashtable
内容,对我来说已经足够了.
Now Pester outputs the Hashtable
content in JSON format, which is good enough for me.
是否有一种更优雅的方式来自定义 Hashtable
的Pester输出,不需要我更改每个测试用例的代码?
Is there a more elegant way to customize Pester output of Hashtable
, which doesn't require me to change the code of each test case?
推荐答案
比我以前的回答是为应该
编写包装函数.
可以使用 System.Management.Automation.ProxyCommand
生成这种包装器,但是需要一点点的缝合才能以与 dynamicparam一起使用的方式生成它.
应该
的code>块.有关详细信息,请参见此答案.
Such a wrapper can be generated using System.Management.Automation.ProxyCommand
, but it requires a little bit of stitchwork to generate it in a way that it works with the dynamicparam
block of Should
. For details see this answer.
包装程序 process
块被修改为将当前管道对象转换为自定义的 Hashtable
派生的类,该类将覆盖 .ToString()
方法,然后将其传递到原始 Should
cmdlet的 process
块.
The wrappers process
block is modified to cast the current pipeline object to a custom Hashtable
-derived class that overrides the .ToString()
method, before passing it to the process
block of the original Should
cmdlet.
class MyJsonHashTable : Hashtable {
MyJsonHashTable ( $obj ) : base( $obj ) {}
[string] ToString() { return $this | ConvertTo-Json }
}
Function MyShould {
[CmdletBinding()]
param(
[Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)]
[System.Object]
${ActualValue}
)
dynamicparam {
try {
$targetCmd = $ExecutionContext.InvokeCommand.GetCommand('Pester\Should', [System.Management.Automation.CommandTypes]::Function, $PSBoundParameters)
$dynamicParams = @($targetCmd.Parameters.GetEnumerator() | Microsoft.PowerShell.Core\Where-Object { $_.Value.IsDynamic })
if ($dynamicParams.Length -gt 0)
{
$paramDictionary = [Management.Automation.RuntimeDefinedParameterDictionary]::new()
foreach ($param in $dynamicParams)
{
$param = $param.Value
if(-not $MyInvocation.MyCommand.Parameters.ContainsKey($param.Name))
{
$dynParam = [Management.Automation.RuntimeDefinedParameter]::new($param.Name, $param.ParameterType, $param.Attributes)
$paramDictionary.Add($param.Name, $dynParam)
}
}
return $paramDictionary
}
} catch {
throw
}
}
begin {
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Pester\Should', [System.Management.Automation.CommandTypes]::Function)
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline()
$steppablePipeline.Begin($PSCmdlet)
} catch {
throw
}
}
process {
try {
# In case input object is a Hashtable, cast it to our derived class to customize Pester output.
$item = switch( $_ ) {
{ $_ -is [Hashtable] } { [MyJsonHashTable] $_ }
default { $_ }
}
$steppablePipeline.Process( $item )
} catch {
throw
}
}
end {
try {
$steppablePipeline.End()
} catch {
throw
}
}
}
要通过包装程序覆盖Pesters Should
,请定义一个全局别名,如下所示:
To override Pesters Should
by the wrapper, define a global alias like this:
Set-Alias Should MyShould -Force -Scope Global
并恢复原始的应该
:
Remove-Alias MyShould -Scope Global
注释:
- 我还将
GetCommand()
的参数从Should
更改为Pester \ Should
,以避免由于别名而递归.不确定是否确实有必要. - 需要最新版本的Pester.Pester 5.0.4失败,但Pester 5.1.1成功测试.
- I have also changed the argument of
GetCommand()
fromShould
toPester\Should
to avoid recursion due to the alias. Not sure if this is actually necessary though. - A recent version of Pester is required. Failed with Pester 5.0.4 but tested successfully with Pester 5.1.1.
这篇关于在Pester测试用例失败时显示哈希表的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!