Running Dusk Tests In Their Own Database With Laravel Sail
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=mysql2DB_HOST=mysql3DB_PORT=33064DB_DATABASE=your-app-testing 5DB_USERNAME=sail6DB_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_page10Illuminate\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_page2RuntimeException: 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: 115 volumes: - - 'sailmysql:/var/lib/mysql' + - 'sailmysqldusk:/var/lib/mysql' 18 networks:19 - sail20 healthcheck:21 test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]22 retries: 323 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 of3306
. - 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: local8 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=33065DB_DATABASE=your-app-testing6DB_USERNAME=sail7DB_PASSWORD=password
And that's it! Your Dusk test suite is now using its own database, completely separate from your local development database.