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
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
finallyis appropriate. - Run the test in a different order mentally.
Show solution
A finally block protects later tests.
<?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.