2

我有一个 PowerShell .ps1 文件,其中包含脚本顶部的函数,然后是调用这些函数的不同命令。我正在使用 Pester 对我的脚本文件进行单元测试。

如何模拟我的 PowerShell .ps1 脚本中的函数?

我曾尝试模拟该函数,但出现“找不到命令”的错误消息。

我还尝试在描述块中添加一个空的“虚拟”函数。这并没有给我上述错误,但它没有正确模拟脚本中的函数。

我有两个文件。一个保存测试,另一个保存函数和对函数的调用。下面是两个例子:

文件1.ps1

Function Backup-Directory([switch]$IsError)
{
    If($IsError)
    {
        Write-Error "Error"
        Exit 1
    }
}

Backup-Directory $true

文件2.Tests.ps1

$here = (Split-Path -Parent $MyInvocation.MyCommand.Path) -replace '\\test', '\main'
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$productionFile = "$here\$sut"

Describe "File1" {

    Context "When the Back-Directory outputs an error." {

        # Arrange
        Mock Back-Directory { }
        Mock Write-Error

        # Act
        & $productionFile
        $hasSucceeded = $?

        # Assert
        It "Calls Backup-Directory" {
            Assert-MockCalled Backup-Directory -Exactly 1 -ParameterFilter {
                $IsError -eq $true
            }
        }

        It "Writes error message." {
            Assert-MockCalled Write-Error -Exactly 1 -ParameterFilter {
                 $Message -eq "Error"
            }
        }

        It "Exits with an error." {
            $hasSucceeded | Should be $false
        }
    }
}
4

1 回答 1

5

我不认为这是可能的。至少在您当前的实现中。不久前我问了同样的问题...... Pester Issue 414

但是您可以将该内部函数拆分为同一目录中的另一个脚本文件,以便您对其进行单元测试和模拟。您只需在主脚本文件中添加该函数即可使用它:

主要功能.ps1:

# Main script
function Main-Function {
    # if debugging, set moduleRoot to current directory
    if ($MyInvocation.MyCommand.Path) {
        $moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path
    }else {
        $moduleRoot = $PWD.Path
    }

    # dot source the inner function
    . "$moduleRoot\Inner-Function.ps1"

    Write-Output "This is the main script. Calling the inner function"

    Inner-Function

    Write-Output "Back in the main script"
}

内部函数.ps1:

function Inner-Function {
    Write-Output "This is the inner function"
}

Main-Function.Tests.ps1:

$moduleRoot =  Split-Path -Parent $MyInvocation.MyCommand.Path 

# Load Testing Function
. "$moduleRoot\Main-Function.ps1"

# Load Supporting Functions
. "$moduleRoot\Inner-Function.ps1"

Describe 'Main Script' {
    Mock Inner-Function { return "This is a mocked function" }

    It 'uses the mocked function' {
        (Main-Function -match "This is a mocked function") | Should Be $true
    }
}

这是一个非常好的方法,因为我们可以对内部函数进行单元测试,并且随着逻辑的增长,向其中添加测试非常容易(并且可以与其他脚本/函数隔离开来)。

内部函数.Tests.ps1:

$moduleRoot =  Split-Path -Parent $MyInvocation.MyCommand.Path 

# Load Testing Function
. "$moduleRoot\Inner-Function.ps1"

Describe 'Inner Function' {
    It 'outputs some text' {
        Inner-Function | Should Be "This is the inner function"
    }
}

这里有两个要点...

无论您当前的执行目录如何,都可以查找相关函数的位置...

if ($MyInvocation.MyCommand.Path) {
        $moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path
    }else {
        $moduleRoot = $PWD.Path
    }

Dot Sourcing 取决于主要功能中的功能以及单元测试文件....... "$moduleRoot\Inner-Function.ps1"

Split-Path -Path $MyInvocation.MyCommand.Path$null用户会话中,但不会$null在从执行上下文中调用时。意思是,您可以在C:\users\nhudacin其中并且仍然正确加载此脚本/模块。否则,您必须始终从与其所在位置相同的目录中执行此脚本/模块,而不使用$MyInvoation变量。

于 2016-05-17T16:41:59.913 回答