The objects a test starts from are its fixture. When several tests share one, build it in setUp(), which runs before each test method; tearDown() runs after each, even when the test failed, to release what setUp() acquired (a transaction in Subsection 4.20.12). The static setUpBeforeClass() and tearDownAfterClass() run once per class, for expensive resources.
<?php
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Shop\{Money, Order, Product};
#[CoversClass(Order::class)]
final class OrderTest extends TestCase {
private Order $order;
private Product $book;
protected function setUp(): void {
$this->book = new Product('BK-PHP-01', 'Modern PHP in Practice', new Money(3990), 25);
$this->order = new Order();
$this->order->add($this->book, 2);
}
public function testAddingTheSameSkuAgainReplacesTheLine(): void {
$this->order->add($this->book, 3);
$this->assertSame(['BK-PHP-01' => 3], $this->order->quantities());
$this->assertEquals(new Money(11970), $this->order->total());
}
public function testEachTestGetsAFreshOrder(): void {
$this->assertSame(['BK-PHP-01' => 2], $this->order->quantities());
}
}Both tests pass (--testdox lists them as sentences, "Each test gets a fresh order"). The second proves the isolation: the first test's change did not survive, because setUp() built a new order. Keep fixtures small; a 40-line setUp() shared by unrelated tests hides what each one needs.