testing php applications

Risky Tests And Global State

Make Hidden Coupling Visible

Globals, superglobals, static properties, environment variables, current working directory, timezone, and process-level configuration can leak between tests. A test that passes alone but fails in the suite often has hidden shared state.

Use Strict Modes Deliberately

PHPUnit exposes command-line and XML configuration for strict output, global state, time limits, and risky-test failure handling. Enable strictness intentionally and fix the underlying coupling rather than relying on test order.

PHP example
<?php

declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class TimezoneTest extends TestCase
{
    private string $originalTimezone;

    protected function setUp(): void
    {
        $this->originalTimezone = date_default_timezone_get();
    }

    protected function tearDown(): void
    {
        date_default_timezone_set($this->originalTimezone);
    }

    public function testApplicationCanUseUtc(): void
    {
        date_default_timezone_set('UTC');

        $this->assertSame('UTC', date_default_timezone_get());
    }
}

tearDown() restores the process state even if an assertion fails. A try/finally block is another good choice when the change is local to one test.

Common Mistakes

  • Leaving changed timezone or environment variables behind.
  • Depending on test order.
  • Writing tests with no assertion or expectation.
  • Allowing unexpected output to hide debugging prints.

What To Practise

  • Recognise common sources of global state.
  • Restore process-level state after tests.
  • Use PHPUnit strict modes to expose risky tests.

Practice

Practice: Isolate A Timezone Test

Plan a test that changes the default timezone without affecting later tests.

Requirements

  • Capture the original timezone.
  • Restore it even if the test fails.
  • Explain why teardown or finally is appropriate.
  • Run the test in a different order mentally.
Show solution

A finally block protects later tests.

PHP example
<?php

declare(strict_types=1);

$original = date_default_timezone_get();

try {
    date_default_timezone_set('UTC');
    echo date_default_timezone_get() . PHP_EOL;
} finally {
    date_default_timezone_set($original);
}

echo date_default_timezone_get() === $original ? 'restored' : 'leaked';
echo PHP_EOL;

// Prints:
// UTC
// restored

PHPUnit setup and teardown hooks are often the natural home for this restoration.