---
title: "Driver matrix — Testing for Laravel | Roundly"
description: "Run the suite on SQLite, Postgres or MySQL from TESTING_DB_* env vars, with isolated probe connections and a ready CI leg."
url: https://roundly-consulting.com/open-source/docs/testing-for-laravel/driver-matrix
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Testing for Laravel](https://roundly-consulting.com/open-source/docs/testing-for-laravel.md)

# Driver matrix

DriverMatrix runs a suite across database drivers, so a SQLite-only run doesn’t miss what the engines disagree on — a LIKE without ESCAPE, for example, is green on Postgres and returns zero rows on SQLite.

```php
use RoundlyConsulting\Testing\Database\DriverMatrix;

// Only in a TestCase that does NOT extend PackageTestCase (which calls it for you):
protected function defineEnvironment($app): void
{
    DriverMatrix::configure($app);
}

// Skip a driver-specific case visibly on the wrong leg:
it('uses jsonb')->skip(fn () => DriverMatrix::driver() !== 'pgsql');
```

PackageTestCase calls DriverMatrix::configure() for you. configure() points the default testing connection at TESTING\_DB\_DRIVER (default: in-memory SQLite with foreign keys on) and registers pgsql and mysql as named connections — present but unreachable off a driver leg, so a gated assertion skips visibly rather than never firing.

TESTING\_DB\_DRIVER must be one of sqlite, pgsql, mysql or mariadb. Anything else — postgres, sqlsrv — throws InvalidArgumentException instead of quietly running the leg on SQLite and skipping every engine-gated test green.

## Environment

| Variable | Default | Purpose |
| --- | --- | --- |
| `TESTING_DB_DRIVER` | sqlite | The leg’s driver: sqlite, pgsql, mysql or mariadb — anything else throws InvalidArgumentException. The only variable that moves the suite off SQLite. |
| `TESTING_DB_HOST` | 127.0.0.1 | Engine host. |
| `TESTING_DB_PORT` | 5432 / 3306 | Engine port (Postgres / MySQL default). |
| `TESTING_DB_DATABASE` | testing | Database name. |
| `TESTING_DB_USERNAME` | testing / root | User (Postgres / MySQL default). |
| `TESTING_DB_PASSWORD` | '' | Password. |

A CI leg must export TESTING\_DB\_DRIVER. The location variables only say where the engine is; exporting them without the driver leaves the whole suite on SQLite — a “pgsql” job that never touches Postgres. The location applies only to the leg’s own driver, so off-leg connections fall back to their defaults and fail fast instead of hanging on the wrong engine. On a SQLite leg it describes the Postgres probe only — a Postgres service beside a SQLite leg; to reach MySQL, run a MySQL leg with TESTING\_DB\_DRIVER=mysql.

## Running locally against Postgres

```bash
TESTING_DB_DRIVER=pgsql \
TESTING_DB_HOST=127.0.0.1 TESTING_DB_PORT=5432 \
TESTING_DB_DATABASE=testing TESTING_DB_USERNAME=testing TESTING_DB_PASSWORD=secret \
vendor/bin/pest
```

## A Postgres CI leg

The package’s own leg now starts Postgres with docker run on tmpfs, durability off — see CI for real-engine legs in Package base test case. A GitHub Actions leg with a Postgres service container works too, ready to lift:

```yaml
test-pgsql:
  runs-on: ubuntu-latest
  name: P8.4 - pgsql real-engine

  services:
    postgres:
      image: postgres:16
      env:
        POSTGRES_DB: testing
        POSTGRES_USER: testing
        POSTGRES_PASSWORD: secret
      ports:
        - 5432:5432
      options: >-
        --health-cmd pg_isready
        --health-interval 10s
        --health-timeout 5s
        --health-retries 5

  steps:
    - uses: actions/checkout@v7
    - uses: shivammathur/setup-php@v2
      with:
        php-version: 8.4
        extensions: pdo, sqlite, pdo_sqlite, pgsql, pdo_pgsql
    - run: composer update --prefer-stable --prefer-dist --no-interaction
    - name: Execute tests against postgres
      env:
        # TESTING_DB_DRIVER is what moves the suite off sqlite.
        TESTING_DB_DRIVER: pgsql
        TESTING_DB_HOST: 127.0.0.1
        TESTING_DB_PORT: 5432
        TESTING_DB_DATABASE: testing
        TESTING_DB_USERNAME: testing
        TESTING_DB_PASSWORD: secret
      run: vendor/bin/pest --ci
```

## Isolated probe connections

The pgsql and mysql connections that configure() registers are isolated probes: on Postgres their search\_path is a dedicated testing\_probe schema, on MySQL a dedicated testing\_probe database, both created on demand. The real-engine runner drops its target clean, and this isolation makes it structurally impossible for a probe run to drop the live suite’s tables.

## Parallel runs

Under pest --parallel each worker has a token (ParallelTesting::token(), paratest’s TEST\_TOKEN). The suite’s real-engine database becomes <database>\_<token> — created on first use over the shared database — and the probe namespace testing\_probe\_<token>, so one worker’s teardown and probe drops never reach another worker. SQLite’s :memory: is per process already; without a token nothing changes.

## API

| Method | Purpose |
| --- | --- |
| `DriverMatrix::configure($app)` | Point the testing connection at the matrix driver and register the pgsql/mysql probe connections. |
| `DriverMatrix::driver()` | The active driver — TESTING\_DB\_DRIVER, defaulting to sqlite; an unknown value throws. Use it in ->skip() guards. |
| `DriverMatrix::connectionConfig(?string $driver = null)` | The resolved connection config for the active (or given) driver — to register it under another name or assert on it. |
| `DriverMatrix::probeConnectionConfig(string $driver)` | The same config confined to the probe namespace — testing\_probe, per worker under --parallel. |
| `DriverMatrix::prepareProbe(string $connection)` | Create the probe namespace if it’s missing; a no-op for any connection that isn’t an isolated probe. |
| `DriverMatrix::isProbe(string $connection)` | Whether a configured connection is an isolated probe — what the real-engine runner requires of a non-SQLite target. |
| `DriverMatrix::probeNamespace()` | testing\_probe, suffixed with the worker’s token under --parallel. |

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/testing-for-laravel.md)
