PowerShell 模块函数无法访问调用者范围内的变量
PowerShell module functions cannot access variables in caller's scope
我将 Pester 与 Selenium WebDriver 结合使用。
WebDriver 在相应 'Describe' 块内的 'BeforeAll' 块中初始化,并将生成的实例分配给 $driver 变量。然后,在 'Describe' 和 'It' 块中,我调用驻留在使用 PowerShell 自动加载的外部 PowerShell 模块中的自定义函数。我希望这些函数可以访问 'BeforeAll' 块中定义的 $driver 变量,但它并没有发生,我收到以下错误消息:
RuntimeException: 不能对空值表达式调用方法。
这是来自 Search.Tests.ps1 Pester 脚本的代码:
Describe "Search for something" -Tag something {
BeforeAll {
$driver = New-WebDriver
$driver.Navigate().GoToUrl('http://example.com')
}
AfterAll {
$driver.Close()
$driver.Dispose()
$driver.Quit()
}
Find-WebElement -Selector ('some_selector')
It "Something is found in search results" {
GetTextFrom-WebElement -Selector ('some_selector') | Should Be 'something'
}
}
Find-WebElement 和 GetTextFrom-WebElement 是使用 $driver CSS 获取元素并提取元素内部文本的辅助函数。
我调查了这个问题并找到了解决方法,但我认为这不是一个优雅的方法。解决方法是在参数块之后的外部 PowerShell 模块中的每个辅助函数中重新定义 $driver,如下所示:
$driver = $PSCmdlet.GetVariableValue('driver')
这样函数就可以看到 $driver 并且一切正常。
我的问题:是否可以做一些事情,使函数始终可以访问 $driver 而无需在每个函数中重新定义驱动程序?
"I expect that these functions [defined in a PowerShell module] have access to $driver variable defined in 'BeforeAll' block..."
他们没有,你可能不应该依赖这种行为,即使他们有。
无法从模块访问 Pester Scriptblocks 中定义的变量
在 BeforeAll{}
、BeforeEach{}
、Context{}
和 It{}
块中定义的变量不能 从下面的模块访问测试 x.Tests.ps1
文件何时被 Invoke-Pester
(reference) 调用。如果 x.Tests.ps1
文件恰好被直接调用(即通过在 ISE 中按 F5),则可以从被测模块访问 BeforeAll{}
中定义的变量。依赖于这种行为会阻止 运行 进行更大批量的测试,因此应该避免。
应避免依赖外部变量的隐式可访问性
您的自定义模块似乎希望 $driver
在模块外部某处定义,并且可以从模块内部隐式访问。这就提出了以下问题:自定义模块的作者打算在哪里定义 $driver
?作为模块中的脚本变量?作为全局变量?对于模块来说,这两个都是非常笨拙的 public 接口,因为很难控制 $driver
的正确值是否确实可用于模块。如果模块确实依赖于这种行为,我建议更改自定义模块以明确接受您的 $driver
对象
(或至少创建该对象所需的信息)。
如果您无法更改自定义模块,您可以将变量引用从 $driver
更改为 $global:driver
。不过,您真的应该尽量避免这种情况,因为以这种方式使用全局变量可能会在某些时候导致各种问题中的任何一种。
我将 Pester 与 Selenium WebDriver 结合使用。 WebDriver 在相应 'Describe' 块内的 'BeforeAll' 块中初始化,并将生成的实例分配给 $driver 变量。然后,在 'Describe' 和 'It' 块中,我调用驻留在使用 PowerShell 自动加载的外部 PowerShell 模块中的自定义函数。我希望这些函数可以访问 'BeforeAll' 块中定义的 $driver 变量,但它并没有发生,我收到以下错误消息:
RuntimeException: 不能对空值表达式调用方法。
这是来自 Search.Tests.ps1 Pester 脚本的代码:
Describe "Search for something" -Tag something {
BeforeAll {
$driver = New-WebDriver
$driver.Navigate().GoToUrl('http://example.com')
}
AfterAll {
$driver.Close()
$driver.Dispose()
$driver.Quit()
}
Find-WebElement -Selector ('some_selector')
It "Something is found in search results" {
GetTextFrom-WebElement -Selector ('some_selector') | Should Be 'something'
}
}
Find-WebElement 和 GetTextFrom-WebElement 是使用 $driver CSS 获取元素并提取元素内部文本的辅助函数。
我调查了这个问题并找到了解决方法,但我认为这不是一个优雅的方法。解决方法是在参数块之后的外部 PowerShell 模块中的每个辅助函数中重新定义 $driver,如下所示:
$driver = $PSCmdlet.GetVariableValue('driver')
这样函数就可以看到 $driver 并且一切正常。
我的问题:是否可以做一些事情,使函数始终可以访问 $driver 而无需在每个函数中重新定义驱动程序?
"I expect that these functions [defined in a PowerShell module] have access to $driver variable defined in 'BeforeAll' block..."
他们没有,你可能不应该依赖这种行为,即使他们有。
无法从模块访问 Pester Scriptblocks 中定义的变量
在 BeforeAll{}
、BeforeEach{}
、Context{}
和 It{}
块中定义的变量不能 从下面的模块访问测试 x.Tests.ps1
文件何时被 Invoke-Pester
(reference) 调用。如果 x.Tests.ps1
文件恰好被直接调用(即通过在 ISE 中按 F5),则可以从被测模块访问 BeforeAll{}
中定义的变量。依赖于这种行为会阻止 运行 进行更大批量的测试,因此应该避免。
应避免依赖外部变量的隐式可访问性
您的自定义模块似乎希望 $driver
在模块外部某处定义,并且可以从模块内部隐式访问。这就提出了以下问题:自定义模块的作者打算在哪里定义 $driver
?作为模块中的脚本变量?作为全局变量?对于模块来说,这两个都是非常笨拙的 public 接口,因为很难控制 $driver
的正确值是否确实可用于模块。如果模块确实依赖于这种行为,我建议更改自定义模块以明确接受您的 $driver
对象
(或至少创建该对象所需的信息)。
如果您无法更改自定义模块,您可以将变量引用从 $driver
更改为 $global:driver
。不过,您真的应该尽量避免这种情况,因为以这种方式使用全局变量可能会在某些时候导致各种问题中的任何一种。