justincdotme's avatar

Test Database for Laravel Dusk (5.4)

I am trying to set up Dusk to use a specific SQLite DB for testing but I cannot get Dusk to use the values in my Dusk specific .env file(s).

I created .env.dusk.local and .env.dusk.testing (I know that I only actually need 1 of the .env.dusk.* files but I am using both just to try to get the darn thing working) and set DB_CONNECTION=sqlite in both of those env's. I even tried adding $app['config']->set('database.default','sqlite'); to the CreatesApplication trait.

Even still, Dusk will only talk to the DB that I have configured in my .env .

I confirmed that Dusk is still using the original .env values by updating my DB_CONNECTION in .env to a test value (foo) and then I immediately saw DB errors in the screenshots. I reset the test DB_CONNECTION value in .env back to my development db (pgsql) and the Dusk test passed. Next, I tried changing the DB_CONNECTION value in .env.dusk.local and .env.dusk.testing to a test value (again using foo) but I did not see any DB errors in the screenshots (foo is not a real connection).

So far, the only techniques that work are:

  1. Manually setting DB_CONNECTION to sqlite in .env and then manually changing it back to pgsql after the tests. (not really an acceptable workflow).

  2. setting 'default' => 'sqlite' in config/database.php. (also not an acceptable workflow)

I noticed another issue after trying option 2 where Laravel was complaining about a missing sessions table even though I had set SESSION_DRIVER=array in both of the .env.dusk.* files. This also seems to confirm that Dusk is ignoring the .env.dusk.* files.

The docs indicate that Dusk will back up .env and then copy .env.dusk.{environment} to .env. This does appear to be happening as I can see the .env contents changing by running tail on .env wile running php artisan dusk. No idea why the application won't use the values in my .env.dusk.local or .env.dusk.testing.

Here are my configs:

from config/database.php

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

from .env.dusk.local and .env.dusk.testing (same contents for both)

APP_ENV=local
APP_KEY=base64:REDACTED
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=https://bookme.justinc.dev

DB_CONNECTION=sqlite

BROADCAST_DRIVER=log
CACHE_DRIVER=array
SESSION_DRIVER=array
QUEUE_DRIVER=sync

from Tests\CreatesApplication trait

<?php

namespace Tests;

use Illuminate\Contracts\Console\Kernel;

trait CreatesApplication
{
    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';

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

        return $app;
    }
}

I've read of others having a similar issues across these forums, Stack Overflow and Github but none of the suggestions seem to fix this issue.

Is there a trick to getting Dusk to use the values from .env.dusk.local (assuming app env is local)? If there is no trick, can anyone see where I am going wrong?

I really appreciate any help!

0 likes
12 replies
martinbean's avatar

@justincdotme You should only specify the environment variables you wish to override in your .env.dusk.local file.

It took me a bit of trial-and-error, but I eventually got Dusk to use a SQLite database of my choice by creating another database entry in my config/database.sqlite and then setting the DB_CONNECTION value to that connection.

In your config/database.php file under the connections key:

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

Your .env.dusk.local file:

DB_CONNECTION=testing

So long as you use the DatabaseMigrations trait in your Dusk test cases, this should migrate and rollback the testing SQLite database on the running of your test suite.

2 likes
justincdotme's avatar

@martinbean Thanks for the reply. I just tried your recommendation but Dusk would still only use the db connection specified in .env and not the one from .env.dusk.local. I ran the Dusk test and it passed after implementing your suggestion into database.php and .env.dusk.local. Next, I did a test where I changed DB_CONNECTION in .envto foo; the test failed immediately with the error that "Database [foo] is not configured". I then changed DB_CONNECTION in .env back to pgsql and it worked, even though I kept DB_CONNECTION=testing in .env.dusk.local.

justincdotme's avatar

Ok, it appears that the DB error from changing the DB_CONNECTION to an invalid value was sort of a red herring. It now seems like Dusk uses BOTH the dev DB connection (from .env) and the connection specified in .env.dusk.local. I can run the test now, using the config that @martinbean recommended BUT, I notice 3 things:

  1. Dusk seems to be using DB_CONNECTION from .env for sessions AND authentication, despite the fact that I have added SESSION_DRIVER=file in .env.dusk.local. I know this because I can see new sessions being created in the sessions table of my dev DB.

  2. Dusk also seems to be using the DB_CONNECTION from my .env.dusk.local. I know this because as a test, I created a user in DB using a model factory and that user does not appear in my dev db (the sessions do show up there, the user did not).

  3. The login test doesn't appear to be accessing the same database that newly created test user is in. In this case, the authentication attempt for the test user fails.

class LoginTest extends DuskTestCase
{
    use DatabaseMigrations;
    /**
     * A basic browser test example.
     *
     * @test
     * @return void
     */
    public function a_user_can_log_in()
    {
        $user = factory(User::class)->create([
            'email' => '[email protected]',
            'password' => bcrypt('secret')
        ]);
        $this->browse(function (Browser $browser) use($user) {
            $browser->visit('/login')
                ->assertSee('Login')
                    ->type('email', $user->email)
                    ->type('password', 'secret')
                    ->press('Login')
                    ->assertPathIs('/');
        });
    }
}

justincdotme's avatar

Alright, I think I have this resolved(ish). I think the main issue was that I expected Dusk to use the test DB for all DB related actions but that won't work because the application is actually running in a different environment than Dusk. The only way that I could get Dusk to work is to use the same DB for Dusk and the Laravel app. This means abandoning the idea of using a test database in .env.dusk.local because yes, I could use model factories to create rows in that database with Dusk BUT my application would use the dev db so the application would never see the new row that was created in the Dusk test.

martinbean's avatar

@justincdotme You shouldn’t have to use the same database for development and testing. You seem to have a configuration issue.

I’d get rid of any .env.dusk.* files, reset your database connection to mysql (or whatever you were using) in your .env file, and then start over.

1 like
justincdotme's avatar

@martinbean I have tried restarting a few times by deleting the .env.dusk* files and setting DB_CONNECTION back to pgsql in my .env. I'm wondering if my server infrastructure is causing issues. My dev server is on another physical machine and I access it via LAN from my workstation. Typical workflow is write code on local machine, PHPStorm then uploads to dev server. I run PhpUnit locally and I have also been running php artisan dusk from my local machine. I can see in the failure screenshots that Dusk is accessing my dev server. I'm wondering if maybe my issue is due to the app and Dusk being run on 2 separate machines. I suppose there would be no way for Dusk to tell the dev server that it also needs to swap .env for .env.dusk.local.

martinbean's avatar
Level 80

@justincdotme Well yes, that would do it. If you’re running the tests from one machine, and trying to test the application on another, it’s not going to work as they’ll be different environments. You need to test the application from the same machine you run the tests from (i.e. locally).

justincdotme's avatar

Alright, that makes total sense. @martinbean I really appreciate your assistance with my issue!

I managed to get around that by using php artisan serve so I thought I'd share my code for anyone else who had this problem.

We'll be using Laravel's php artisan serve command to run PHP's built in server locally.

First, add a new test connection to config/database.php

'test' => [
            'driver' => 'sqlite',
            'database' => database_path('testdb.sqlite'),
            'prefix' => '',
        ],

Next, modify the .env.dusk.local file so that it looks like this:

APP_URL=http://127.0.0.1:8000
DB_CONNECTION=test

Finally, create a blank test db in database/ by running touch database/testdb.sqlite. This file will act as a test DB during testing. Now just run php artisan serve to start the local webserver.

The only problem that I ran into was that artisan serve seemed to hang on to the initial state of the .env file, which makes sense. But now Dusk and Laravel were interacting with 2 different databases because the original DB info was in the primary .env.

I was able to get around this by setting the environment var in bash by running export DB_CONNECTION=test php artisan serve which did the trick.

spekkie2002's avatar

Hi guys,

I just spent 3 hours on this stuff... When you would like to work with two databases, and run your application with php artisan serve you could also try my favorite method:

(i stole some of the code from @justincdotme to save some time, thanks man :-) )

Add a new test connection to config/database.php

'test' => [
    'driver' => 'sqlite',
    'database' => database_path('testdb.sqlite'),
    'prefix' => '',
],

Next, copy your .env file, and name it.env.dusk.local, and change the parameters below:

APP_ENV=testing
APP_URL=http://127.0.0.1:8001 
DB_CONNECTION=test

Then, start your regular artisan server with php artisan serve which will start at port 8000. You will use this one for manual testing/viewing in your browser.

After that, start the test-server that will be used by dusk with php artisan serve --env=dusk.local --port=8001

I hope it helps!

3 likes
gedanken's avatar

@spekkie2002 after many hours of frustration this finally solve my issue with dusk reading from 2 different databases. It will now assert from data seeded within thr test!! Just had to ensure to run config:cache followed by cache:clear before running artisan serve in one termnial and dusk in another. php artisan serve --env=dusk local --port=8000 (had to match the port to what dusk was looking for) then ran php artisan dusk --env=dusk.local (in a second terminal). This took WAY too long to find a solution for!! Btw: it would not work for me without specifying the port as well as the env on serve.

justincdotme's avatar

@spekkie2002 Nice work, I like your technique. I didn't even know that was possible to specify the env for artisan serve. (mindblown)

1 like
jijoel's avatar

For dusk to work, if you're using the standard config/database.php file, you need to explicitly overwrite the DB_DATABASE environment variable. Your .env.dusk.local file should include this:

DB_CONNECTION=sqlite
DB_DATABASE=database/bookme.sqlite
1 like

Please or to participate in this conversation.