0

Unfortunately config(['key' => 'newValue']) doesn't work in a Dusk setup (for overriding a config value), presumably because it would change the config of the system running the test rather than the experience of the headless browser that gets opened to execute the flow.

And sometimes I can see no way around needing to temporarily change an env value for a certain Dusk test.

E.g. temporarily set QUEUE_DRIVER=sync when usually it is 'dusk-connection', but in one particular test, I need to check for values in the 'jobs' tables in the DB.

Before upgrading to Laravel >=5.8 (and therefore newer versions of DotEnv), I was able to use this function called within a Dusk test before $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://stackoverflow.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!');
    }
}

I was able to call it like this: $this->overrideDuskEnv(['QUEUE_DRIVER' => 'sync']);

But in more recent Laravel versions, environment variables are immutable (see "Read-Only env Helper").

How can I achieve my goal, where Dusk uses .env.dusk.local for most tests but then for certain tests may differ slightly?

Ryan
  • 22,332
  • 31
  • 176
  • 357

4 Answers4

0

Finally after struggling with this problem for 10+ hours, I have a solution.

/**
 * @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!');
    }
}

Then in my setUp() function in my Dusk test class, I call:

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

Then in each test function after the closing of $this->browse(function (Browser $browser)... and before assertions, I call:

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.

The tricky part to understand with Dusk is that the environment variables (and therefore the config arrays) of the console process running the tests differ from those that get used by the headless browser (simulating what a real user would experience).

By the way, I had been so hopeful about approaches like this one, but they turned out to be complete wastes of time because DuskCommand is already calling overload to make the env variables mutable.

Ryan
  • 22,332
  • 31
  • 176
  • 357
0

See here for a documented approach to overriding config([]) during a dusk test:

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

digout
  • 4,041
  • 1
  • 31
  • 38
0

You can also use a seperate env for all the dusk tests. It is also mentioned in the laravel docs here https://laravel.com/docs/8.x/dusk#environment-handling

Saransh Kumar
  • 421
  • 4
  • 6
0

This is a bit easy but if you put any wrong entry then it will burst.

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();
}

Using

<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>
Puneet Sharma
  • 307
  • 3
  • 9