Fix test failures
Diagnose the test-infrastructure traps that recur in this repository — container networking, tenancy lifecycle, Pest bindings, and template databases.
Test failures
Use the reported symptom to distinguish test-environment failures from an App regression. An assertion failure still needs investigation in the App; this page does not classify every failure as harness trouble.
For failures caused by missing tenant context or grants, see Fix tenant context problems.
Symptom index
| Symptom | Section |
|---|---|
could not translate host name "postgres" at bootstrap | Pest cannot reach the database |
A relation is null inside TenantCreated | Tenant lifecycle events fire mid-create |
| Parent and child test bindings collide | uses in matches recursively |
| A synthetic user resolver is ignored | Auth overwrites a synthetic user resolver |
| Tests run against a stale schema | Template databases did not rebuild |
| Package tests are not discovered | Package tests are not discovered |
A test passes alone but fails with --parallel | Shared state across workers |
Pest cannot reach the database
Cause. ./vendor/bin/pest from the host shell uses your host PHP, outside the
docker compose network. It fails during bootstrap, before any assertion.
Fix.
task test -- packages/people/tests/Feature/PeoplePublicIntegrationContractTest.php --compactConfirm. Tests execute and report assertions.
Prevent. Do not override DB_HOST=127.0.0.1. The override is scoped to that
one command and does not leak, but it aims the suite at an endpoint that is not
the test database — .env.testing names the postgres compose service.
Tenant lifecycle events fire mid-create
A relation is null inside TenantCreated.
Cause. Tenant::create() fires TenantCreated while the row is still mid-build.
Relations accessed inside that event cache as null, because the related rows do not
exist yet.
Fix. Defer relation access to a stage that runs after CreateTenantAction
completes database setup. Do not set the ready flag during Tenant::create() —
let CreateTenantAction toggle it after setup, or the creation pipeline breaks.
Confirm. The relation resolves in the later stage.
Prevent. Treat TenantCreated as "the row exists", not "the tenant is ready".
uses in matches recursively
Parent and child Pest test bindings collide.
Cause. Pest's uses()->in() walks recursively, so a parent directory binding
collides with a child directory binding.
Fix. Use glob patterns instead of directory paths when test directories nest, so the binding stops at one level.
Confirm. Each test file receives exactly one binding.
Prevent. A package suite binds once in tests/Pest.php with
uses(TestCase::class)->in(__DIR__). Add nesting deliberately.
Auth overwrites a synthetic user resolver
The resolved request user is not the synthetic user assigned by the test.
Cause. When a test binds a synthetic request with
app()->instance('request', $request), Laravel's auth integration may re-attach
request state and overwrite an earlier setUserResolver().
Fix. Order matters:
- Bind the request into the container.
- Then call
$request->setUserResolver(...).
The resolver must attach after auth has re-read the container.
Confirm. The resolved user is the synthetic one throughout the request.
Prevent. Keep the two calls adjacent and in that order.
Template databases did not rebuild
Tests run against a stale schema after a schema change.
Cause. The bootstrap replaces per-test migrate:fresh with PostgreSQL template
cloning. It builds {DB_DATABASE}_tpl_c_{hash} (central) and _tpl_t_{hash} (tenant
chain) once per hash over the migration filenames and content, plus the tenant
schema dump, then every test clones from
them.
The hash covers those inputs only. A schema change produced outside a migration file — an env-dependent migration branch, a manual edit to a template database — does not trigger a rebuild, and tests run against a stale schema.
Fix. First stop other test runs before dropping shared test templates. Express every test-visible schema change as a migration file edit; the hash change rebuilds both templates on the next run. To force a rebuild:
docker compose exec postgres psql -U postgres -c "SELECT 'DROP DATABASE ' || quote_ident(datname) || ';' FROM pg_database WHERE datname LIKE 'nexia_test%\_tpl\_%'" -t -A | docker compose exec -T postgres psql -U postgresConfirm. The next run rebuilds templates and the new column or constraint is visible.
Prevent. No schema change outside a migration file. This is the trap that produces the most confusing symptom — a migration you can read in the file, absent from the database.
Package tests are not discovered
The package test path is absent from a full task test run.
Cause. The runner discovers only package test paths whose Composer package is both required by the root project and installed.
Fix. Check three things:
| Check | Where |
|---|---|
extra.nexia.test_paths: ["tests"] | package composer.json |
autoload-dev namespace declared | package composer.json |
| Package required and installed | activate it, then task rebuild |
Confirm. task test includes your package's tests; task test -- packages/people/tests
runs them alone.
Prevent. Activation is what makes a package a root requirement.
Shared state across workers
A test passes alone but fails with --parallel.
Cause. Paratest workers have separate PHP processes, central databases, and tenant prefixes. Static properties and singletons can leak between tests in one worker, but are not shared across workers. Parallel-only failures can also come from shared files, cache keys, or other external resources.
Fix. Identify the shared resource named by the failure. Namespace files and external keys per worker and clean them up. Restore in-process registry mutations after each test; the SDK Approval composer registry provides resetForTests() for that purpose.
Confirm. The test passes both alone and with --parallel.
Prevent. Keep test state local and restore registry mutations. Use Test an App to select the smallest relevant run; broaden to parallel execution when investigating worker-dependent behavior.
Related
- Test an App — what to test and how to run it
- Fix tenant context problems — when the cause is context or grants
- Data Model and Migrations — why schema changes belong in migration files