MySQL Database
For production-like testing or when your app requires MySQL-specific features, you can use MySQL in CI.
Two Patterns
| Pattern | Use Case |
|---|---|
| Service Container | Run MySQL in the CI runner |
| External Connection | Connect to existing MySQL server |
Pattern A: MySQL Service in Runner
GitHub Actions can run MySQL as a service container alongside your tests.
Service Configuration
yaml
jobs:
browser-tests:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testing
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3Configure Laravel Connection
yaml
- name: Configure database
run: |
echo "DB_CONNECTION=mysql" >> .env
echo "DB_HOST=127.0.0.1" >> .env
echo "DB_PORT=3306" >> .env
echo "DB_DATABASE=testing" >> .env
echo "DB_USERNAME=root" >> .env
echo "DB_PASSWORD=password" >> .envComplete Workflow
yaml
name: Browser Tests
on: [push, pull_request]
jobs:
browser-tests:
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testing
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Checkout API
uses: actions/checkout@v4
with:
path: backend
- name: Checkout Frontend
uses: actions/checkout@v4
with:
repository: your-org/frontend-repo
path: frontend
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: dom, curl, libxml, mbstring, zip, pdo_mysql
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- name: Install Composer dependencies
run: composer install --no-interaction --prefer-dist
- name: Install npm dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Prepare Laravel
run: |
cp .env.example .env
php artisan key:generate
echo "DB_CONNECTION=mysql" >> .env
echo "DB_HOST=127.0.0.1" >> .env
echo "DB_PORT=3306" >> .env
echo "DB_DATABASE=testing" >> .env
echo "DB_USERNAME=root" >> .env
echo "DB_PASSWORD=password" >> .env
php artisan migrate --force
- name: Run browser tests
run: ./vendor/bin/pest tests/BrowserPattern B: External MySQL Connection
Connect to an existing MySQL server (e.g., cloud database, shared testing server).
Using Secrets
Store credentials as repository secrets:
- Go to repository → Settings → Secrets and variables → Actions
- Add secrets:
DB_HOSTDB_DATABASEDB_USERNAMEDB_PASSWORD
Workflow Configuration
yaml
- name: Configure database
run: |
echo "DB_CONNECTION=mysql" >> .env
echo "DB_HOST=${{ secrets.DB_HOST }}" >> .env
echo "DB_PORT=3306" >> .env
echo "DB_DATABASE=${{ secrets.DB_DATABASE }}" >> .env
echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> .env
echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> .envSecurity
Never commit database credentials. Always use GitHub Secrets.
Pest Configuration
Same as SQLite - use DatabaseTruncation:
php
// tests/Pest.php
<?php
use TestFlowLabs\PestPluginBridge\Bridge;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Tests\TestCase;
pest()->extends(TestCase::class)
->use(DatabaseTruncation::class)
->in('Browser');
Bridge::setDefault('http://localhost:3000')
->serve('npm run dev', cwd: '../frontend');phpunit.xml Configuration
For MySQL, update your phpunit.xml:
xml
<phpunit>
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="mysql"/>
<!-- Other values come from .env in CI -->
</php>
</phpunit>MariaDB Alternative
MariaDB is a drop-in replacement for MySQL:
yaml
services:
mariadb:
image: mariadb:10.11
env:
MARIADB_ROOT_PASSWORD: password
MARIADB_DATABASE: testing
ports:
- 3306:3306
options: >-
--health-cmd="healthcheck.sh --connect --innodb_initialized"
--health-interval=10s
--health-timeout=5s
--health-retries=3Troubleshooting
Connection Refused
- Check the service health check passed
- Verify port mapping (
3306:3306) - Ensure
DB_HOST=127.0.0.1(notlocalhost)
Access Denied
- Check
MYSQL_ROOT_PASSWORDmatchesDB_PASSWORD - Verify
DB_USERNAME=root
Slow Startup
MySQL takes time to initialize. The health check ensures it's ready before tests run.