Функции модуля PowerShell не могут получить доступ к переменным в области действия вызывающего

Я использую Pester с Selenium WebDriver. WebDriver инициализируется в блоке BeforeAll в соответствующем блоке Describe, а результирующий экземпляр назначается переменной $ driver. Затем в блоках «Описание» и «Оно» я вызываю свои пользовательские функции, которые находятся во внешнем модуле PowerShell, который автоматически загружается с помощью PowerShell. Я ожидаю, что эти функции имеют доступ к переменной $ driver, определенной в блоке BeforeAll, но этого не происходит, и я получаю следующее сообщение об ошибке:

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 и извлечения внутреннего текста элемента.

Я изучил проблему и нашел обходной путь, но не думаю, что это элегантный способ. Обходной путь - переопределить $ driver в каждой вспомогательной функции во внешнем модуле PowerShell сразу после блока параметров следующим образом:

$driver = $PSCmdlet.GetVariableValue('driver')

Таким образом, функции могут видеть $ driver и все работает.

Мой вопрос: можно ли что-то сделать, чтобы функции всегда имели доступ к $ driver без необходимости переопределять драйвер в каждой из них?


person YMM    schedule 03.09.2016    source источник
comment
Не могу понять, чем здесь может помочь InModuleScope. Вспомогательные функции экспортируются, они не являются частными.   -  person YMM    schedule 03.09.2016
comment
Вы должны иметь возможность определить $driver вне блока Describe, чтобы изменить его область действия.   -  person Eris    schedule 03.09.2016
comment
Но мне нужно инициализировать драйвер в рамках процедуры настройки теста, поэтому он находится в блоке BeforeAll. Может быть, есть способ изменить его область действия, оставив инициализацию в «BeforeAll»?   -  person YMM    schedule 03.09.2016
comment
Да, $driver = $null снаружи, тогда инициализируйте его. Я также рекомендую Set-Strictmode -Version latest, чтобы избежать неопределенных переменных.   -  person Eris    schedule 03.09.2016


Ответы (1)


«Я ожидаю, что эти функции [определенные в модуле PowerShell] имеют доступ к переменной $ driver, определенной в блоке 'BeforeAll' ...»

Они этого не делают, и вам, вероятно, не стоит полагаться на такое поведение, даже если они и сделали.

Переменные, определенные в блоках скриптов Pester, недоступны из модулей

Переменные, определенные в блоках _1 _, _ 2 _, _ 3_ и It{}, недоступны из тестируемого модуля, когда файл x.Tests.ps1 вызывается Invoke-Pester (ссылка). Если файл x.Tests.ps1 вызывается напрямую (например, нажатием F5 в ISE), тогда переменные, определенные в BeforeAll{}, доступны из тестируемого модуля. Использование такого поведения исключает запуск этого теста в больших пакетах, поэтому его следует избегать.

Следует избегать использования неявной доступности внешних переменных

Похоже, ваш настраиваемый модуль ожидает, что $driver определен где-то вне модуля и неявно доступен изнутри модуля. Возникает следующий вопрос: где автор настраиваемого модуля намеревался определить $driver? В качестве переменной скрипта в модуле? Как глобальная переменная? Оба они представляют собой довольно неудобные общедоступные интерфейсы для модуля, потому что трудно контролировать, действительно ли для модуля доступно правильное значение для $driver. Если модуль действительно полагается на такое поведение, я предлагаю изменить настраиваемый модуль, чтобы он явно принимал ваш объект $driver (или, по крайней мере, информацию, необходимую для создания этого объекта).

Если вы не можете изменить настраиваемый модуль, вы можете обойтись изменением ссылок на переменные с $driver на $global:driver. Однако вам действительно следует попытаться избежать этого, потому что использование глобальных переменных таким образом, вероятно, в какой-то момент приведет к любой из множества проблем.

person alx9r    schedule 05.09.2016
comment
Спасибо за столь подробное объяснение! Изменение $ driver на $ global: driver не работает, функции в модуле по-прежнему не могут получить доступ к значению переменной. Но я передам эту переменную $ driver в вызове функции - это работает. - person YMM; 06.09.2016