Skip to content
Troubleshooting

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

SymptomSection
could not translate host name "postgres" at bootstrapPest cannot reach the database
A relation is null inside TenantCreatedTenant lifecycle events fire mid-create
Parent and child test bindings collideuses in matches recursively
A synthetic user resolver is ignoredAuth overwrites a synthetic user resolver
Tests run against a stale schemaTemplate databases did not rebuild
Package tests are not discoveredPackage tests are not discovered
A test passes alone but fails with --parallelShared 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.

Code example
Shell
task test -- packages/people/tests/Feature/PeoplePublicIntegrationContractTest.php --compact

Confirm. 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:

  1. Bind the request into the container.
  2. 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:

Code example
Shell
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 postgres

Confirm. 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:

CheckWhere
extra.nexia.test_paths: ["tests"]package composer.json
autoload-dev namespace declaredpackage composer.json
Package required and installedactivate 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.

Source of truth: docs/developers/content/en/troubleshooting/test-failures.md