Как использовать Pester для макетирования функций в анонимных расширенных функциях

Недавно я обнаружил, что мне не нужно использовать Import-Module для использования моих расширенных функций powershell, я могу просто определить функцию анонимно в файле ps1.

К сожалению, мои юнит-тесты Пестера не пройдены. Я не могу больше издеваться над вызовом New-Object в списке ниже. Как правило, я бы поставил точку в приведенном ниже коде и определил бы функцию Get-StockQuote в своей области видимости. Теперь точечный источник файла ps1 не помогает, потому что я все равно вызываю функцию через имя файла.

Как я могу использовать Pester для тестирования приведенного ниже кода с фиктивной реализацией 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 {
}

2 ответа

Решение

Поэтому я нашел способ сделать это, определив именованную функцию с тем же именем, что и имя файла в блоке 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 {
 }

Таким образом, после точечного получения моего файла ps1 у меня будет определение функции в области видимости, и Пестер начнет работать соответствующим образом.

$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 (файлы.ps1) выполняются в собственной области действия, называемой областью сценариев. Так что я думаю, что Пестеру будет сложно высмеивать командлеты, которые он использует.

В обходном пути вы вынуждены объявить функцию, которая видит смоделированную версию командлета.

Другие вопросы по тегам