Skip to content
Latchkey

PHPUnit: Test Was Run in a Separate Process and Crashed in CI

PHPUnit can run a test in an isolated child process (via @runInSeparateProcess or process isolation). The child re-bootstraps and serializes global state into it. When bootstrap differs, constants are redefined, or state is non-serializable, the child crashes and the test errors out - often only in CI.

What this error means

A test using @runInSeparateProcess errors with output about the separate process, a serialization failure, or "Cannot redeclare"/constant-already-defined in the child. The same test passes when not isolated.

phpunit
1) App\Tests\PaymentTest::testWebhook
This test was run in a separate process and the process crashed.
PHP Fatal error: Constant APP_BOOTED already defined

Common causes

preserveGlobalState re-runs bootstrap with side effects

With global state preserved, the child re-includes bootstrap that defines constants or boots a framework again, hitting "already defined"/"cannot redeclare".

Non-serializable state cannot cross the process boundary

Closures, resources, or objects with live connections cannot be serialized into the child, so the isolated run crashes.

How to fix it

Disable global state preservation for the isolated test

Tell PHPUnit not to serialize global state into the child so bootstrap is not double-applied.

php
/**
 * @runInSeparateProcess
 * @preserveGlobalState disabled
 */
public function testWebhook(): void { /* ... */ }

Guard bootstrap against re-definition

Make constant/define and framework-boot idempotent so a second include in the child is safe.

php
if (!defined('APP_BOOTED')) {
    define('APP_BOOTED', true);
}

Avoid isolation unless it is truly needed

  1. Use @runInSeparateProcess only for tests that mutate global/static state.
  2. Keep non-serializable resources out of preserved global state.
  3. Confirm the same bootstrap is used for the parent and child processes.

How to prevent it

  • Reserve process isolation for tests that genuinely need a clean process.
  • Pair @runInSeparateProcess with @preserveGlobalState disabled by default.
  • Make bootstrap idempotent (guard defines and framework boot).

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →