您如何确保脚本或模块所依赖的所有模块都已显式导入?

How do you ensure that all modules a script or module depends on have been explicitly imported?

我发现 . @briantist 。这对我来说似乎是个好建议。问题是我还没有找到一种可靠的方法来确保显式导入所有必需的模块。

例子

假设我有三个具有这些依赖项的模块:

m1.psm1 (depends on nothing)
m2.psm1 (calls functions in m1)

还假设这些模块中的每一个都有一组实现 100% 代码覆盖率的自动化测试。

运行 m2 的测试仅在 PowerShell 的自动加载碰巧无法加载 m2 的罕见情况下提醒您缺少对 Import-Module m1 的调用在具体时间你运行测试。如果您有几十个 .psm1 文件,则丢失所需 Import-Module 的几率会急剧增加。

假设我在m2.psm1中错误地省略了Import-Module m1(毕竟它通过了所有的自动化测试)并将这些模块投入生产。每次执行 m2 中的函数时,m1 都有可能不会自动加载。当 m1 没有成功自动加载时,该次脚本无法正确执行,但可能会在之前和之后的时间正确执行。这将使诊断问题的原因变得相当困难。

如何确保脚本或模块所依赖的所有模块都已显式导入?


澄清:我正在开发的模块主要是自动化,最终将 运行 在服务器上无人值守。因此,我需要确保模块中的功能在部署到我的开发工作站以外的机器上后能够快速正确地执行。

当然,可以在启动 powershell 会话时导入您明确需要的所有模块。但是,自动加载机制仍然掩盖了您忘记显式加载您所依赖的模块的情况。

你也可以在启动时盲目加载所有模块(我在开发的某些阶段这样做),但这意味着启动时间大约为 10 秒,随着模块的大小而增长图书馆。启动时间可能会成为一个问题,因为每次执行脚本时都会产生启动时间。

这是我个人的做法,为了比较,我在个人资料中有

# Get the contents of the module folder
$modules = Get-ChildItem "$PSScriptRoot\modules" -Directory

# For each item
ForEach($module in $modules)
{
    Try
    {
        # Try loading the module in the folder
        Import-Module $module.FullName
    }
    Catch
    {
        # Oh darn
        Write-Warning "Could not load $module"
    }
}

在我的模块文件中,我也有在最上面

#requires -version 3
#requires -module MyModule

Get-Module MyModule | Select -ExpandProperty RequiredModules | Import-Module

为 Bluecakes 的回答投票++。不过,我的偏好是使用数组来控制模块列表,也就是说,不要只加载在模块路径中找到的模块。个人喜好,我知道。此外,我建议在导入之前删除模块 - 否则 PS 将在当前会话中为您缓存它们,使开发变得尴尬(即:您修改了一个模块,但它不会重新加载)。

我最终确定了以下策略来检测任何未明确导入的模块:

  • 使用 $PSModuleAutoLoadingPreference='none' 来阻止 powershell 环境自动加载任何东西。
  • 切勿从文本装置导入任何模块。
  • 在每次测试 运行 之前删除尽可能多的模块。

纠缠公约

为了实现上述目标,我为 Pester 测试装置确定了以下约定:

Remove-egModules
$Global:mut = 'egMyModule'
Import-Module $mut

InModuleScope $mut {
    Describe{
        BeforeEach{ Remove-egModules -Except $mut }
        It 'does something useful' {
            ...
        }
    }
}

Remove-Variable 'mut' -Scope Global

此外,测试夹具所需但被测模块不需要的任何 "helper" 模块都必须导入被测模块,尽管它们仅在测试夹具中需要。为什么?假设您在测试夹具中导入了一个辅助模块,并且该辅助模块也恰好被被测模块使用。即使您没有在被测模块中显式导入帮助模块,测试也会成功。当测试夹具导入模块时,它也可用于被测模块。

移除-egModules

我确定我需要在 运行 测试时重复删除所有模块,除了最少量的模块。这是因为模块似乎在整个 powershell 会话中保持加载状态。为此,我使用了这个函数:

function Remove-egModules
{
    [CmdletBinding()]
    param
    (
        # this module does not get removed
        $Except 
    )
    process
    {
        Get-Module | 
            ? { $_.Name -ne 'egRemoveModule' } | # the module where this function is
            ? { 
                ...   # test for any other modules that should be removed
            } |
            ? { $_.Name -ne $Except } | 
            Remove-Module |
            Out-Null
    }
}