I decided to switch from SQLite to MySQL for Laravel testing. You can read about the reasons here.
I always use Docker and VSCode’s devcontainer for my development environment. Therefore, I needed to update my Docker Compose file and add a database service specifically for when running pest
.
For running pest
, I created a separate service and run it with docker compose run --rm -it pest
.
I also grouped it with cli
as a profile so that it does not run automatically when bringing up the entire container using docker compose up -d
.
pest:
image: serversideup/php:8.1-cli
working_dir: /var/www/html
volumes:
- .:/var/www/html/:cached
environment:
- PUID=${UID:-1000}
- PGID=${GID:-1000}
entrypoint: ./vendor/bin/pest
profiles:
- cli
Now, let’s add the database service for testing only.
pest:
image: serversideup/php:8.1-cli
working_dir: /var/www/html
volumes:
- .:/var/www/html/:cached
environment:
- PUID=${UID:-1000}
- PGID=${GID:-1000}
entrypoint: ./vendor/bin/pest
profiles:
- cli
depends_on:
- mariadb-testing
mariadb-testing:
image: mariadb:10.11
tmpfs: /var/lib/mysql
environment:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
MARIADB_DATABASE: testing
healthcheck:
test: ["CMD", "mysqladmin", "ping"]
interval: 10s
timeout: 5s
retries: 3
profiles:
- cli
We need to ensure that pest
only runs after mariadb-testing
is up, which is why we added the depends
rule for the pest
service.
Before running it, let’s also adjust phpunit.xml
.
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_HOST" value="mariadb-testing"/>
<env name="DB_DATABASE" value="testing"/>
<env name="DB_USERNAME" value="root"/>
Now, let’s run it, and… we encounter an error.
SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for mariadb-testing failed: Temporary failure in name resolution (Connection: mysql, SQL: SHOW FULL TABLES WHERE table_type = 'BASE TABLE')
SQLSTATE[HY000] [2002] Connection refused (Connection: mysql, SQL: SHOW FULL TABLES WHERE table_type = 'BASE TABLE')
Even though the database service is already running, it’s not ready for the connection. However, after a few retries, the test passes.
Therefore, the healthcheck
in mariadb-testing
is not effective, as it gives a false positive result.
The solution is to add another service using the busybox
image, which essentially waits for the database to be fully ready for connection.
pest:
image: serversideup/php:8.1-cli
working_dir: /var/www/html
volumes:
- .:/var/www/html/:cached
environment:
- PUID=${UID:-1000}
- PGID=${GID:-1000}
entrypoint: ./vendor/bin/pest
profiles:
- cli
depends_on:
wait-for-mariadb-testing:
condition: service_completed_successfully
mariadb-testing:
image: mariadb:10.11
tmpfs: /var/lib/mysql
environment:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
MARIADB_DATABASE: testing
profiles:
- cli
wait-for-mariadb-testing:
image: busybox:latest
depends_on:
- mariadb-testing
command:
[
"sh",
"-c",
"until nc -vz mariadb-testing 3306 ; do echo 'waiting for mariadb-testing:3306' ; done",
]
profiles:
- cli
Now, when running the pest
service, the testing will wait until the database is fully ready 🎉.
Tips
I’ve created some bash aliases to help me run docker compose
and docker compose run
.
dc() {
docker compose "$@"
}
dr() {
docker compose run --rm -it -u $(id -u):$(id -g) "$@"
}
Now, I just need to run these from my project folder.
dc up -d # up all services
dc down # down all services
dr pest # run specific service
dr artisan # another service