Yesterday, I was trying to step up Laravel Dusk to test our Laravel web app to make sure that the pages are not crashing for a Vue frontend using Inertia for our releases. But, I ran into many problems, and it took way more time than I expected to set up in Sail. I am posting this so that it could help someone else.
I am using alias
sail
as alias for./vendor/bin/sail
throughout.
First, install Laravel Dusk.
sail composer require --dev laravel/dusk
sail php artisan dusk:install
We are going to customize the Laravel Sail default docker configuration, so publish the Sail docker file.
sail artisan sail:publish
Sail's default mysql
service persists the MySQL data to the local machine so that it is available after restarting the container. This is not needed for a test database, so we are going to create a new MySQL service named mysql.test
for testing purposes. Since we have are going to use two both Laravel Test(Phpunit) and Laravel Dusk in our app we need two separate databases for each one. Dusk will use the database dusk_test
and Phpunit will take laravel_test
.
By default, Dusk will take the database connection specified .env
file. Since we don't want Dusk to change our data in the development database, we are going to specify a different database for Dusk. Dusk has a feature that replaces the .env
file with .env.dusk.{environment}
when we run the tests. We will specify the Laravel Dusk test configuration in the .env.dusk.local
file as below.
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:EUzC7XceXTxeQwT+eRMEysZwrs6e/pHjO2xYUNZTggo=
APP_DEBUG=true
APP_URL=http://laravel.test
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=mysql.test
DB_DATABASE=dusk_test
DB_USERNAME=sail
DB_PASSWORD=password
The database for Dusk in the above is configured to use a different database port 3307
as the port 3306
is assigned to the mysql
service and we changed the database name to dusk_test
.
Now, we need the Sail to have the above-configured database service to be up and running for the tests to run. Add new service mysql_test
with the configuration below.
mysql.test:
image: "mysql:8.0"
ports:
- "${FORWARD_DB_PORT:-3307}:3306"
environment:
MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"
MYSQL_DATABASE: "${DB_DATABASE}_test"
MYSQL_USER: "${DB_USERNAME}"
MYSQL_PASSWORD: "${DB_PASSWORD}"
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
volumes:
- "./docker/mysql.test/init:/docker-entrypoint-initdb.d"
tmpfs:
- "/var/lib/mysql"
networks:
- sail
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
retries: 3
timeout: 5s
Important things in the above config:
- We have configured the default port to be
3307
to not conflict withmysql
service. - The database name is
${DB_DATAbase}_test
. This database is going to be used by PHPUnit. - Since
mysql
docker image supports only one database in configuration, but we need two databases for tests, we are going to create a sql script in the path./.docker/mysql.test/init/01-database.sql
to create additional databases. This script will be copied to the container path/docker-entrypoint-initdb.d
and will be run during initialization. - For volume, we are using a
tmpfs
as the data don't have to be persisted.
The file ./docker/mysql.test/init/01-create_dusk_database.sql
in the project should have the script:
/* create databases */
CREATE DATABASE IF NOT EXISTS `dusk_test`;
/* give access to database */
GRANT ALL PRIVILEGES ON dusk_test.* TO 'sail'@'%';
Restart the sail by executing sail up
command. You can see that mysql.test
started and it has two databases *_test
and dusk_test
databases.
Create a .env.testing
config file for specifying the PHPUnit test configuration. The database name will be the database name in your .env file and add _test
at the end. No need to give the
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:EUzC7XceXTxeQwT+eRMEysZwrs6e/pHjO2xYUNZTggo=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=mysql.test
# Below database name should your database name in .env file and add `_test`
DB_DATABASE=*_test
DB_USERNAME=sail
DB_PASSWORD=password
Now we have completed all the configurations. It's time to test now!
Update the class in file tests/Feature/ExampleTest.php
with below:
class ExampleTest extends TestCase
{
use RefreshDatabase;
public function test_example()
{
User::factory()->create();
$response = $this->get('/');
$response->assertStatus(200);
$this->assertDatabaseCount('users', 1);
}
}
Replace the function class in file tests/Browser/ExampleTest.php
with the below code
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
public function testBasicExample()
{
User::factory()->create();
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertSee('Laravel');
});
}
}
Run both Phpunit and Dusk tests and see them both passing with database access.
sail test
Debugging Tips
- Check whether all the required services have started.
- Check whether our SQL script is copied into the container(
sail shell
). - Check database connections and the databases in it using a database client in your host machine.
- Check whether you can
curl http://laravel.test
from inside your containerlaravel.test
. - Running
php artisan dusk
from inside the container won't work by default as Sail is doing some additional things behind the scene (usesail dusk
).
Additional information
- If you add
DUSK_DRIVER_URL=http://selenium:4444/wd/hub
to your.env.dusk.local
file you can run the dusk from inside the container usingphp artisan dusk
.
Top comments (1)
what would the phpunit.xml file look like using this setup?