Fixtures, setUp and tearDown

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.

tests/Unit/OrderTest.php, a fixture rebuilt before every testPHP
<?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.