在Pester测试用例失败时显示哈希表的内容 [英] Show content of hashtable when Pester test case fails

查看:68
本文介绍了在Pester测试用例失败时显示哈希表的内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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() from Should to Pester\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测试用例失败时显示哈希表的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆