简体   繁体   English

如何使用 Pester 模拟匿名高级函数中的函数

[英]How to use Pester to mock functions in anonymous advanced functions

I recently discovered that I don't need to use Import-Module to use my advanced powershell functions, I can just define a function anonymously in a ps1 file.我最近发现我不需要使用 Import-Module 来使用我的高级 powershell 函数,我可以在 ps1 文件中匿名定义一个函数。

Unfortunately, my Pester unit tests are broken.不幸的是,我的 Pester 单元测试坏了。 I can't seem to mock the call to New-Object in the listing below anymore.我似乎无法再在下面的列表中模拟对 New-Object 的调用。 Typically, I would dot source the below code and have the function Get-StockQuote defined in my scope.通常,我会点源以下代码并在我的范围内定义函数 Get-StockQuote。 Now dot sourcing the ps1 file does not help because I invoke the function via the file name anyway.现在点采购 ps1 文件没有帮助,因为无论如何我都是通过文件名调用该函数的。

How can I use Pester to test the below code with a mock implementation of New-Object?我如何使用 Pester 来测试以下带有 New-Object 模拟实现的代码?

Note: This code is obviously trivial for the purpose of the question, the tests for code I am working with really does need a mock implementation of New-Object.注意:对于这个问题,这段代码显然是微不足道的,我正在使用的代码测试确实需要一个 New-Object 的模拟实现。

# Source listing of the file: Get-StockQuote.ps1
<#
.Synopsis
Looks up a stock quote
.Description
Uses the yahoo api to retrieve a recent quote for a given stock.
.Parameter Symbol
The stock trading symbol
.Example
Get-StockQuote.ps1 -Symbol AMZN
Prints the following line to the output
440.84
#>
[CmdletBinding()]
Param(
    [parameter(Mandatory=$false)]
    [string]$Symbol
)
BEGIN {
    Set-StrictMode -Version 1
}
PROCESS {
    (New-Object System.Net.WebClient).DownloadString("http://finance.yahoo.com/d/quotes.csv?s=$Symbol&f=l1")
}
END {
}

So I found a way to do this by defining a named function by the same name as the file name in the BEGIN block and invoking it from the PROCESS block.所以我找到了一种方法,通过在 BEGIN 块中定义一个与文件名同名的命名函数并从 PROCESS 块调用它。

[CmdletBinding()]
Param(
    [parameter(Mandatory=$false)]
    [string]$Symbol
)

 BEGIN {
    Set-StrictMode -Version 1
    Function Get-StockQuote {
        [CmdletBinding()]
        Param(
            [parameter(Mandatory=$false)]
            [string]$Symbol
        )
        BEGIN{}
        PROCESS{
            (New-Object System.Net.WebClient).DownloadString("http://finance.yahoo.com/d/quotes.csv?s=$Symbol&f=l1")
        }
        END{}
    }
 }
 PROCESS {
    Get-StockQuote @PSBoundParameters
 }
 END {
 }

This way, after dot sourcing my ps1 file, I will have the function definition in scope and Pester will start working appropriately.这样,在 dot 采购我的 ps1 文件后,我将在作用域中定义函数,Pester 将开始正常工作。

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")

. "$here\$sut"

Describe "Get a stock quote" {
    Mock New-Object {
        $retval = [pscustomobject]@{}
        Add-Member -InputObject $retval -MemberType ScriptMethod DownloadString {
            param( [string] $url )
            if ($url -imatch 'AMZN') {
                return 500.01
            }
            return 100.00
        }
        return $retval
    } -ParameterFilter {$TypeName -and ($TypeName -ilike 'System.Net.WebClient') }
    Context "when called for AMZN" {
        $result = Get-StockQuote -Symbol AMZN
        It "Should RETURN 500.01" {
            $result | should be 500.01
        }
    }
    Context "when called for anything else" {
        $result = Get-StockQuote -Symbol MSFT
        It "Should RETURN 100.00" {
            $result | should be 100.00
        }
    }
}

PowerShell scripts (.ps1 files) run in their own scope called Script scope. PowerShell 脚本(.ps1 文件)在它们自己的范围内运行,称为脚本范围。 So I think it would be difficult for Pester to mock the cmdlets it uses.所以我认为 Pester 很难模拟它使用的 cmdlet。

In the workaround you are forced to declare a function which sees the mocked version of the cmdlet.在解决方法中,您被迫声明一个函数,该函数可以看到 cmdlet 的模拟版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM