Dusk: Dusk env file not being loaded correctly

Created on 2 Feb 2017  路  22Comments  路  Source: laravel/dusk

I have the following two files: .env and .env.dusk.local

.env:

APP_ENV="local"
APP_KEY=base64:358E4UyIq+Zbg+H+Pm03dYA2B/vz3ViPJ+U48ikg6qE=
APP_DEBUG=true
APP_LOG_LEVEL="debug"
APP_URL="http://localhost"

DB_CONNECTION="mysql"
DB_HOST="127.0.0.1"
DB_DATABASE="homestead"
DB_USERNAME="homestead"
DB_PASSWORD="secret"

CACHE_DRIVER="redis"
SESSION_DRIVER="redis"
QUEUE_DRIVER="sync"

.env.dusk.local

APP_ENV="local"
APP_KEY=base64:358E4UyIq+Zbg+H+Pm03dYA2B/vz3ViPJ+U48ikg6qE=
APP_DEBUG=true
APP_LOG_LEVEL="debug"
APP_URL="http://localhost"

DB_CONNECTION="sqlite"

CACHE_DRIVER="file"
SESSION_DRIVER="file"
QUEUE_DRIVER="sync"

But when I run php artisan dusk I get errors about the sqlite database not existing.
Illuminate\Database\QueryException: Database (homestead) does not exist. (SQL: select * from sqlite_master where type = 'table' and name = migrations)

So, I suspect that the env files are not correctly being copied and swapped into place. It seems they are simple being merged together, which is causing issues for tests not working.

I am currently using this on Manjaro Linux, and have not tried it on any other OS.

Most helpful comment

@francoislevesque not necessarily. Dusk will backup your .env and move .env.dusk.{APP_ENV} to .env so the browser can match the same environment as yours. This, however, fails to happen when using php artisan serve because serve will cache your env variables the moment of starting to serve. This can be solved by running php artisan serve --env=dusk.local (switch local for anything you use locally). But all of you are suffering from different kinds of problem somewhat related to the same thing. If everyone were using serve, it would make sense to implement a dusk:serve, perhaps.

All 22 comments

What version of Dusk are you using? Is it the latest one (1.0.5) ?

Yes. v1.0.5

I'm also having the same issue. I now set DB_DATABASE as :memory: for the testing environment and I didn't get the above error. Now I'm getting:

testing.ERROR: PDOException: SQLSTATE[HY000]: General error: 1 no such table: table_name in ......../vendor/laravel/framework/src/Illuminate/Database/Connection.php:445

I also tried adding $this->artisan('migrate'); to the setUp method, but still the same.

I had a similar issue using Homestead, which was permissions related. Dusk would dump the contents of .env.dusk.local into my .env but fail to perform the backup/replace, leaving my .env over-written with test settings.

In your case, I don't think it can write into your .env period, which means it'll just be using your local settings.

I believe the issue is in the code below, as well as restoreEnvironment(). For example, given you have a .env.dusk.local (returned by $this->duskFile()), the code assumes this gets written into .env, and moves on with the tests:

protected function backupEnvironment()
    {
        // this can fail due to permissions
        copy(base_path('.env'), base_path('.env.backup'));
       // this can also fail. In my case, since the file exists, the write did succeed
        copy(base_path($this->duskFile()), base_path('.env'));
    }

// at this point restoreEnvironment() will obviously fail since no backup was made

I would personally prefer this code to throw an exception if the backup/replace is not working.

That might be causing your issue - to verify, run a test php script from the command line which copies files in the app root, writes into an existing file, and performs an unlink. My fix was a vagrant reload, but it will depend on your setup.

Your .env file is replaced with .env.dusk.local before the browser is "opened", so when the browser starts, the DB_DATABASE is not defined on your temporary .env file (copy of .env.dusk.local).

At this moment you will need to define all variables in your .env.dusk.local same as .env and then change only the values you need.

A better solution is make a merge instead of a replacement, but I think this need changes on the laravel core and not the dusk package.

I have a similar problem. If you create a .env.dusk.testing/local, and you dont define a DB_DATABASE, assuming it would just default to the database.php env('DB_DATABASE', database_path('database.sqlite')), things dont work as expected. I was suddenly seeing the DB_DATABASE from my .env file (which pointed to a pgsql database name), as if the .env.dusk file was read on top of my .env file.

If I defined a DB_DATABASE filename in the .env.dusk file, things started working fine.

For those trying to use :memory, that wont work. You need to use a normal sqlite file.

This is still an issue

I opted for making another database array entry in config/database.php

'dusk-sqlite' => [
    'driver' => 'sqlite',
    'database' => database_path('database.sqlite'), // env() in sqlite option was using .env value
    'prefix' => '',
],

and used this in my .env.dusk.local

DB_CONNECTION=dusk-sqlite

@taylorotwell Looks like env() is being used from .env file before dusk file is loaded. Is the environment loaded via .env before .env.dusk.local is copied to .env?

@bbashy I am not sure about your last assumption. Or maybe we're talking about different issues.

But anyway, I tried running Dusk today while overriding some of the env values and as a result I don't even have APP_KEY from main .env file loaded in tests and my APP_DEBUG is assumed to be false while .env explicitly states it to be true. 馃槃

Indeed, it seems like Dusk replaces the contents of .env file in it's entirety rather than performing merge and the working workflow is as follows:

cp .env .env.dusk.local
sed -i "s/DB_CONNECTION=.*/DB_CONNECTION=sqlite/" .env.dusk.local
sed -i "s#DB_DATABASE=.*#DB_DATABASE=/opt/project/database/database.sqlite#" .env.dusk.local

But this all sounds very confusing to me after overriding env variables in phpunit.xml. I think it should be clearly stated in the docs how exactly Dusk processes .env file.

Having a similar/related issue

I'm using Homestead on a Windows machine. When running php artisan dusk, the .env's content is replaced by .env.dusk.local's content. However, the tests still use the "old" .env.

My .env

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:6R5VxSoy2djjIQ0ah6BUCgBmrLNpdOcr37LemBag8wg=
APP_URL=http://laravel-boilerplate.dev

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

My .env.dusk.local

APP_NAME=Laravel_test
APP_ENV=local
APP_KEY=base64:6R5VxSoy2djjIQ0ah6BUCgBmrLNpdOcr37LemBag8wg=
APP_URL=http://laravel-boilerplate.dev

DB_CONNECTION=testing

config/database.php

'testing' => [
            'driver' => 'mysql',
            'host' => '127.0.0.1',
            'port' => '3306',
            'database' => 'testing',
            'username' => 'homestead',
            'password' => 'secret',
            'unix_socket' => '',
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],

While running the tests

LoginTest.php

    use DatabaseMigrations;

    private $user;

    public function test_login_interact()
    {
        var_dump(env('APP_NAME'));
        $this->user = factory(User::class)->create([
            'password' => bcrypt('secret')
        ]);

        $this->browse(function (Browser $browser) {
            $browser->visit('/login')
                    ->waitForText('Remember Me')
                    ->type('email', $this->user->email)
                    ->type('password', 'secret')
                    ->click('input[type="submit"]')
                    ->waitFor('.panels')
                    ->assertPathIs('/home');
        });
    }

The var_dump of APP_ENV is Laravel_test, which is fine.

However, the DatabaseMigrations trait is doing its migrations on the homestead database instead of testing.

Also, in my blade file, I'm doing the following:
<h1>{{ config('app.name') }}</h1>

The output on the dusk screenshot is "Laravel" instead of "Laravel_test".
capture

Partial Solution

Adding this line in tests/Browser/CreatesApplication.php to force the database change worked for me:

public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';

        $app->make(Kernel::class)->bootstrap();
        $app['config']->set('database.default','testing');

        return $app;
    }

However, it seems chrome is still using the old database connection since I still see "Laravel" instead of "Laravel_test" in the <h1>.

As @bbashy said, it looks like env() is being used from .env file before dusk file is loaded.

Edit
It worked perfectly on anoter machine (Windows 10, similar specs, vagrant 1.9.2, Homestead 5.2.3). Still trying to figure out what's wrong on the first machine.

@bbashy @adiachenko @francoislevesque are you all using php artisan serve prior to running Dusk?

@deleugpn No, never use it.

@deleugpn I am using Docker. It's not that I treat this as a bug, it's just that the specifics aren't emphasized in documentation, hence confusion.

@deleugpn No, not using it. Should we?

@francoislevesque not necessarily. Dusk will backup your .env and move .env.dusk.{APP_ENV} to .env so the browser can match the same environment as yours. This, however, fails to happen when using php artisan serve because serve will cache your env variables the moment of starting to serve. This can be solved by running php artisan serve --env=dusk.local (switch local for anything you use locally). But all of you are suffering from different kinds of problem somewhat related to the same thing. If everyone were using serve, it would make sense to implement a dusk:serve, perhaps.

@deleugpn agree. Running a separate artisan serve with specifying environment in a different port --env=dusk.local works well. php artisan dusk:serve will be very good.

@mustafaaloko I already did and got #249 and then I tried #250 to make it possible to easily extend, but also no luck because of the construct actions. Furthermore, I tried https://github.com/laravel/framework/pull/18973 to make #250 look better, but also no. Now I just gave up.

I have the same problem,

thanks @deleugpn for pointing that out so clearly.

In case this helps anyone, here is the problem I was having: https://github.com/laravel/dusk/issues/211#issuecomment-322835162

I came by a very similar problem. I had DB_DATABASE as homestead in .env.dusk.local and when any of my tests accessed a route that was querying the database, I would get:

Database (homestead) does not exist.

I finally solved by changing:

    'sqlite' => [
        'driver' => 'sqlite',
        'database' => env('DB_DATABASE', database_path('database.sqlite')),
        'prefix' => '',
    ],

To this:

    'sqlite' => [
        'driver' => 'sqlite',
        'database' => database_path(env('DB_DATABASE', 'database.sqlite')),
        'prefix' => '',
    ],

It's all explained in this answer.

Closing this issue because it's already solved, old or not relevant anymore. Feel free to reply if you're still experiencing this issue.

Having a similar/related issue

I'm using Homestead on a Windows machine. When running php artisan dusk, the .env's content is replaced by .env.dusk.local's content. However, the tests still use the "old" .env.

My .env

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:6R5VxSoy2djjIQ0ah6BUCgBmrLNpdOcr37LemBag8wg=
APP_URL=http://laravel-boilerplate.dev

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

My .env.dusk.local

APP_NAME=Laravel_test
APP_ENV=local
APP_KEY=base64:6R5VxSoy2djjIQ0ah6BUCgBmrLNpdOcr37LemBag8wg=
APP_URL=http://laravel-boilerplate.dev

DB_CONNECTION=testing

config/database.php

'testing' => [
            'driver' => 'mysql',
            'host' => '127.0.0.1',
            'port' => '3306',
            'database' => 'testing',
            'username' => 'homestead',
            'password' => 'secret',
            'unix_socket' => '',
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],

While running the tests

LoginTest.php

    use DatabaseMigrations;

    private $user;

    public function test_login_interact()
    {
        var_dump(env('APP_NAME'));
        $this->user = factory(User::class)->create([
            'password' => bcrypt('secret')
        ]);

        $this->browse(function (Browser $browser) {
            $browser->visit('/login')
                    ->waitForText('Remember Me')
                    ->type('email', $this->user->email)
                    ->type('password', 'secret')
                    ->click('input[type="submit"]')
                    ->waitFor('.panels')
                    ->assertPathIs('/home');
        });
    }

The var_dump of APP_ENV is Laravel_test, which is fine.

However, the DatabaseMigrations trait is doing its migrations on the homestead database instead of testing.

Also, in my blade file, I'm doing the following:
<h1>{{ config('app.name') }}</h1>

The output on the dusk screenshot is "Laravel" instead of "Laravel_test".
capture

Partial Solution

Adding this line in tests/Browser/CreatesApplication.php to force the database change worked for me:

public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';

        $app->make(Kernel::class)->bootstrap();
        $app['config']->set('database.default','testing');

        return $app;
    }

However, it seems chrome is still using the old database connection since I still see "Laravel" instead of "Laravel_test" in the <h1>.

As @bbashy said, it looks like env() is being used from .env file before dusk file is loaded.

Edit
It worked perfectly on anoter machine (Windows 10, similar specs, vagrant 1.9.2, Homestead 5.2.3). Still trying to figure out what's wrong on the first machine.

I had the same problem and solved by this solution ... 馃憤

Fixed this issue today by adding into the phpunit.dusk.xml file

  </testsuites>

  <php>
    <env name="APP_ENV" value="testing" force="true"/>
    <env name="CACHE_DRIVER" value="redis" force="true"/>
    <env name="SESSION_DRIVER" value="redis" force="true"/>
    <env name="QUEUE_DRIVER" value="redis" force="true"/>
    <env name="DB_CONNECTION" value="mysql" force="true"/>
    <env name="DB_DATABASE" value="testing" force="true"/>
    <env name="TELESCOPE_ENABLED" value="false"/>
  </php>

</phpunit>

UPDATE
I also had to change my .env.dusk.local to .env.dusk.testing

  • php artisan cache:clear --env=testing
  • php artisan cache:clear
Was this page helpful?
0 / 5 - 0 ratings

Related issues

hackel picture hackel  路  6Comments

dmitryuk picture dmitryuk  路  4Comments

alex-LE picture alex-LE  路  7Comments

guirociozanini picture guirociozanini  路  6Comments

subathanikaikumaran picture subathanikaikumaran  路  3Comments