expectException() declares, before the code runs, that the rest of the test must throw; expectExceptionMessage() and expectExceptionCode() narrow it. To inspect the exception's own properties, such as OutOfStockException::$available, catch it yourself and call $this->fail() after the line that should have thrown.
<?php
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
use PHPUnit\Framework\TestCase;
use Shop\{Money, Order, OutOfStockException, Product};
final class OrderStockTest extends TestCase {
public function testOrderingMoreThanTheStockThrows(): void {
$this->expectException(OutOfStockException::class);
$this->expectExceptionMessage('BK-UX-01: only 7 left');
new Order()->add(new Product('BK-UX-01', 'Calm Interfaces', new Money(3400), 7), 8);
}
#[IgnoreDeprecations]
public function testTheFloatFactoryIsDeprecated(): void {
$this->expectUserDeprecationMessage(
'Method Shop\Money::fromFloat() is deprecated since 2.0, use fromDecimal()');
$this->assertSame(1999, Money::fromFloat(19.99)->cents);
}
}Since PHPUnit 10 79,198 , PHP warnings, notices and deprecations are no longer turned into exceptions: the test keeps running, the report lists them as issues, and the failOn* flags decide the outcome. fromFloat() carries PHP 8.4's #[\Deprecated] attribute, which raises E_USER_DEPRECATED; expectUserDeprecationMessage() asserts it, and #[IgnoreDeprecations] keeps the expected notice out of the report. Both tests pass.