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

时间:2015-03-12 22:16:39

标签: powershell module

我发现module auto-loading is unreliable。 @briantist recommends explicitly importing required modules in finished code。这对我来说似乎是个好建议。问题是我还没有找到一种可靠的方法来确保明确导入所有必需的模块。

实施例

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

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

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

运行m2的测试只会提醒您缺少对Import-Module m1的调用,因为PowerShell的自动加载很少会导致m2无法加载.psm1您运行测试的具体时间。如果您有几个Import-Module个文件,则错过必需Import-Module m1的几率会急剧增加。

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

如何确保明确导入脚本或模块所依赖的所有模块?


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

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

您也可以在启动时盲目加载所有模块(我在开发期间的某些阶段执行此操作),但这取决于这意味着启动时间大约为10秒,随着模块库的大小而增长。启动时间可能会成为问题,因为每次执行脚本都会产生启动时间。

3 个答案:

答案 0 :(得分:1)

这就是我亲自做的事情,相比之下,我的个人资料

# 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

答案 1 :(得分:1)

我最终选择了以下策略来检测尚未显式导入的任何模块:

  • 使用$PSModuleAutoLoadingPreference='none'停止powershell环境自动加载任何内容。
  • 永远不要从文本夹具中导入任何模块。
  • 在运行每个测试之前删除尽可能多的模块。

Pester Conventions

为了达到上述目的,我确定了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

此外,任何"帮助"测试夹具所需的模块,但被测模块不需要的模块必须导入被测模块,尽管它们仅在测试夹具中需要。为什么?假设您在测试夹具内导入辅助模块,并且该辅助模块也恰好被测试模块使用。即使您未在测试模块中明确导入辅助模块,测试也会成功。当测试夹具导入模块时,它也可供测试模块使用。

移除-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
    }
}

答案 2 :(得分:0)

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