Как переопределить переменные env в Laravel Dusk

К сожалению config(['key' => 'newValue'])не работает в настройке Dusk (для переопределения значения конфигурации), предположительно потому, что это изменило бы конфигурацию системы, запускающей тест, а не опыт безголового браузера, который открывается для выполнения потока.

И иногда я не вижу возможности временно изменить значение env для определенного теста Dusk.

Например, временно установлен QUEUE_DRIVER=sync когда обычно это "сумеречное соединение", но в одном конкретном тесте мне нужно проверить значения в таблицах "заданий" в БД.

Перед обновлением до Laravel >=5.8 (и, следовательно, более новых версий DotEnv) я мог использовать эту функцию, вызываемую в тесте Dusk раньше $this->browse(...:

/**
 * Overrides any .env variables for Dusk tests. https://laracasts.com/discuss/channels/testing/how-to-change-env-variable-config-in-dusk-test 
 * The changes exist only for that one test because of tearDown.
 * Remember that you need to be using `php artisan dusk` instead of `phpunit`.
 * https://stackru.com/questions/54407784/laravel-dusk-how-to-change-config-values-before-each-test-for-the-browser#comment103224655_54407784
 *
 * @param array $variables
 */
protected function overrideDuskEnv($variables = []) {
    $path = self::DOT_ENV;
    if (file_exists($path)) {
        $contentToPrepend = '';
        foreach ($variables as $key => $value) {// Convert all new parameters to expected format
            $contentToPrepend .= $key . '="' . $value . '"' . PHP_EOL;
        }
        $originalFileContents = $this->envContents;
        $comment = '# ==============================================' . PHP_EOL . '# VARIABLES ABOVE THIS LINE are from "' . __FUNCTION__ . '" function in DuskTestCase ( https://laracasts.com/discuss/channels/testing/how-to-change-env-variable-config-in-dusk-test )' . PHP_EOL;
        file_put_contents($path, $contentToPrepend . $comment . $originalFileContents); //"If they are appended, it doesn't seem to take priority."
    } else {
        throw new \Exception('Could not find env file to override!');
    }
}

Я смог назвать это так: $this->overrideDuskEnv(['QUEUE_DRIVER' => 'sync']);

Но в более поздних версиях Laravel переменные среды неизменяемы (см. "Env Helper только для чтения").

Как я могу достичь своей цели, где Dusk использует .env.dusk.local для большинства тестов, но тогда для некоторых тестов могут незначительно отличаться?

5 ответов

Наконец, после 10+ часов борьбы с этой проблемой у меня есть решение.

/**
 * @param array $variables
 */
protected function overrideDuskEnv($variables = []) {
    $path = self::DOT_ENV;
    if (file_exists($path)) {
        $contentToAppend = '';
        foreach ($variables as $key => $value) {// Convert all new parameters to expected format
            $contentToAppend .= $key . '="' . $value . '"' . PHP_EOL;
        }
        $originalFileContents = $this->envContents;
        $comment = '# ==============================================' . PHP_EOL . '# VARIABLES BELOW THIS LINE are from "' . __FUNCTION__ . '" function in DuskTestCase ( https://laracasts.com/discuss/channels/testing/how-to-change-env-variable-config-in-dusk-test )' . PHP_EOL;
        $this->baseCommand->consoleOutput('Appending to ' . $path . ': ' . $contentToAppend);
        file_put_contents($path, $originalFileContents . $comment . $contentToAppend); //It used to be the case that "If they are appended [rather than prepended], it doesn't seem to take priority", but after the DotEnv upgrade in Laravel 5.8, it seems prepending doesn't work and appending does.
    } else {
        throw new \Exception('Could not find env file to override!');
    }
}

Тогда в моем setUp() в моем тестовом классе Dusk я вызываю:

    $this->overrideDuskEnv([
        'SIGNUP_FORM_POST_PATH' => \App\Helpers\Routes::SIGNUP,
        'QUEUE_DRIVER' => \App\Helpers\ConfigConstants::DUSK_CONNECTION
    ]);

Тогда в каждой тестовой функции после закрытия $this->browse(function (Browser $browser)... и перед утверждениями я вызываю:

config(['queue.default' => \App\Helpers\ConfigConstants::DUSK_CONNECTION]); //this does not affect the headless browser but IS probably necessary here so that assertQueued knows to pull from the queue that the headless browser was using.

Сложность, которую нужно понять с помощью Dusk, заключается в том, что переменные среды (и, следовательно, массивы конфигурации) консольного процесса, выполняющего тесты, отличаются от тех, которые используются браузером без головы (имитируя то, что может испытать реальный пользователь).

Кстати, я так надеялся на подходы, подобные этому, но они оказались пустой тратой времени, потому что DuskCommand уже звонитoverload чтобы сделать переменные env изменяемыми.

См. Здесь документированный подход к переопределению config([]) во время сумеречного теста:

https://gist.github.com/wrabit/e01df16858505c395b7b0d271112a023

Вы также можете использовать отдельный env для всех тестов в сумерках. Это также упоминается в документации laravel здесь https://laravel.com/docs/8.x/dusk#environment-handling

Вы можете использовать пакет под названием «dumcapiconf» .

Этот пакет добавляет в ваше приложение маршруты, управляющие конфигурациями. Посещая эти маршруты во время тестирования, вы можете изменять конфигурации во время выполнения тестирования.

Вот пример того, как это выглядит при использовании в вашем тесте:

      public function testEnrollmentOpen(): void
{
    $this->browse(function (Browser $browser) {

    #  ⭐ Use duskapiconf to setConfig ⭐ 
    $this->setConfig('app.openEnrollment', true);
            
    $browser->visit('/')->assertPresent('@enrollment-open');
    });
}

В этом видео объясняется, как настроить и использовать пакет: Конфигурации динамической среды в Laravel Dusk (duskapiconf).

Это немного просто, но если вы введете неправильную запись, она лопнет.

      public function store(Request $request) {
    foreach ($request->all() as $key => $value) {
        $_ENV[$key] = $value;
    }
    $x = '';
    unset($_ENV['_token']);
    foreach ($_ENV as $key => $value) {
        $x .= $key . "=" . $value . "\n";
    }
    base_path('.env');
    file_put_contents(base_path('.env'), $x);
    return redirect()->back();
}

С использованием

      <form class="grid gap-2" action="{{ route('admin.enviroment.store') }}" method="post">
    <div>
        <x-label for="GOOGLE_TAG"  :value="__('GOOGLE_TAG')" />
        <x-input id='GOOGLE_TAG' name="GOOGLE_TAG" :value="__($_ENV['GOOGLE_TAG'])" class="w-full rounded-md dark:bg-gray-700" type="text" required  />
    </div>
    @csrf
    <x-button class="ml-auto dark:bg-blue-900/90">
        {{ __('Update GOOGLE TAG') }}
    </x-button>
</form>
Другие вопросы по тегам