Test Lifecycle
Most non-trivial tests need some setup before they run — an object to test against, a database connection, sample data — and some cleanup afterward. Rather than repeating that setup code in every single test method, PHPUnit calls a handful of special methods automatically at the right points in a test class's lifecycle.
setUp() and tearDown()
setUp() runs before every single test method in the class, and tearDown()
runs after every single test method. If a class has four test methods, setUp() runs four
times — once immediately before each one.
| Method | Runs | Typical use |
|---|---|---|
setUp() | Before every test method | Create a fresh object under test, open a fresh connection |
tearDown() | After every test method | Close connections, delete temporary files, reset global state |
setUpBeforeClass() and tearDownAfterClass()
These two are different: they are static methods that run only once for the
whole class — setUpBeforeClass() before any test method runs, and tearDownAfterClass()
after the very last one finishes. They are the right place for expensive, one-time setup that every test in
the class can safely share, such as starting a test database container or loading a large fixture file.
Worked Example
Here, setUp() creates a brand-new Calculator instance before each test method
runs, storing it in a property so every test method can use $this->calculator:
<?php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
private Calculator $calculator;
protected function setUp(): void
{
// Runs before EVERY test method - each test gets its own fresh instance
$this->calculator = new Calculator();
}
public function testAddsTwoNumbers()
{
$this->assertEquals(5, $this->calculator->add(2, 3));
}
public function testSubtractsTwoNumbers()
{
// A completely fresh $calculator, unaffected by the previous test
$this->assertEquals(1, $this->calculator->subtract(3, 2));
}
protected function tearDown(): void
{
// Runs after every test method - nothing to clean up for a simple Calculator
unset($this->calculator);
}
public static function setUpBeforeClass(): void
{
// Runs ONCE before any test method in this class - good for expensive shared setup
echo "Starting CalculatorTest suite\n";
}
public static function tearDownAfterClass(): void
{
// Runs ONCE after all test methods in this class have finished
echo "Finished CalculatorTest suite\n";
}
}
Why Fresh State Per Test Matters
Because setUp() rebuilds $this->calculator before every test method, one test
can never accidentally leak state into another. This matters more than it might first appear: PHPUnit does
not guarantee your test methods run in the order they're written, so tests that secretly depend on state
left behind by an earlier test can pass or fail depending on run order alone. Fresh state per test keeps
every test method fully independent and its result reproducible no matter what order the suite runs in.