Chris
Chris White Web Developer

Running Dusk Tests In Their Own Database With Laravel Sail

22 November 2021 ~2 minute read

Laravel Sail and Laravel Dusk are an awesome combination. Out of the box you get a fully featured development environment powered by Docker, and a testing suite that uses a real web browser to interact with your application as if it were a real user clicking around. Sail even has full support for running Dusk tests inside docker by using the sail dusk command. However, by default, the Dusk test suite will use your local development database to run tests against. This is generally not what you want since your development database is likely filled with shitty data that you don't want to interfere with your tests.

The way you'd instruct Dusk to use a different database for testing is to create an entirely new environment file called .env.dusk.local at the root of your project. In here, you can set completely different database configuration options:

1DB_CONNECTION=mysql
2DB_HOST=mysql
3DB_PORT=3306
4DB_DATABASE=your-app-testing
5DB_USERNAME=sail
6DB_PASSWORD=password

You might think that's the end of it. But when running your tests you'll be faced with nothing but errors:

1PHPUnit 9.5.10 by Sebastian Bergmann and contributors.
2
3EEEEEEEEEEEEEE 14 / 14 (100%)
4
5Time: 00:00.182, Memory: 30.00 MB
6
7There were 14 errors:
8
91) Tests\Browser\SignupTest::it_shows_signup_page
10Illuminate\Database\QueryException: SQLSTATE[HY000] [1044] Access denied for user 'sail'@'%' to database 'your-app-testing' (SQL: select * from information_schema.tables where table_schema = your-app and table_name = migrations and table_type = 'BASE TABLE')

The problem is that although you've instructed Dusk to use a database called your-app-testing, Dusk doesn't automatically create this database when you run your tests. You could go and create this database manually, but that's just another thing to remember to do that could be automated. You might think you could write some code in the DuskTestCase.php file to create the database automatically:

1/**
2 * Prepare for Dusk test execution.
3 *
4 * @beforeClass
5 * @return void
6 */
7public static function prepare()
8{
9 DB::statement('CREATE DATABASE IF NOT EXISTS your-app-testing');
10 
11 if (! static::runningInSail()) {
12 static::startChromeDriver();
13 }
14}

but this just leads to another error because the framework isn't yet initialized:

11) Tests\Browser\SignupTest::it_shows_signup_page
2RuntimeException: A facade root has not been set. in /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:258

The nicer way to solve this is to add an entirely new MySQL container to your environment, used only for tests. Then you can utilize the feature of the MySQL docker container to automatically create the database for you. Copy paste the existing mysql container definition from docker-compose.yml and paste it right underneath to make a new container:

-mysql:
+mysql-dusk:
3 image: 'mysql/mysql-server:8.0'
4 ports:
- - '${FORWARD_DB_PORT:-3306}:3306'
+ - '3307:3306'
7 environment:
8 MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
9 MYSQL_ROOT_HOST: "%"
- MYSQL_DATABASE: '${DB_DATABASE}'
+ MYSQL_DATABASE: 'your-app-testing'
12 MYSQL_USER: '${DB_USERNAME}'
13 MYSQL_PASSWORD: '${DB_PASSWORD}'
14 MYSQL_ALLOW_EMPTY_PASSWORD: 1
15 volumes:
- - 'sailmysql:/var/lib/mysql'
+ - 'sailmysqldusk:/var/lib/mysql'
18 networks:
19 - sail
20 healthcheck:
21 test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
22 retries: 3
23 timeout: 5s

The Git diffs in that snippet show what you need to change, but here's a summary:

  • The container has to have a unique name, we called it mysql-dusk.
  • The exposed MySQL ports must be different from the port your application uses, we've used 3307 instead of 3306.
  • The MYSQL_DATABASE environment variable has been changed to whatever you want the database that the tests get run in to be called. This is where the magic happens.
  • We told it to use a different volume than your regular MySQL container. If you don't do this you'll get all sorts of weird issues as your two databases will be trying to use the same filesystems.

Since you're using a new volume here, you also need to add the volume to the bottom of the file under the volumes key:

1volumes:
2 sailmysql:
3 driver: local
+ sailmysqldusk:
+ driver: local
6 sailredis:
7 driver: local
8 sailmeilisearch:
9 driver: local

Finally, update your .env.dusk.local to use the new container that you made:

1DB_CONNECTION=mysql
-DB_HOST=mysql
+DB_HOST=mysql-dusk
4DB_PORT=3306
5DB_DATABASE=your-app-testing
6DB_USERNAME=sail
7DB_PASSWORD=password

And that's it! Your Dusk test suite is now using its own database, completely separate from your local development database.

Made with Jigsaw and Torchlight. Hosted on Netlify.