testing php applications
Testing Exceptions And Errors
Failure behaviour is part of an API. Tests should prove that invalid input fails in the intended way and that unexpected exceptions are not silently swallowed.
Expect The Right Failure
In PHPUnit, declare the expected exception before calling the code that should throw. Check the exception class and, when stable and meaningful, its message or code.
Do Not Catch Too Much
Production code should catch an exception only when it can add context, translate it at a boundary, retry safely, or recover. Tests should make accidental swallowing visible.
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
function positiveQuantity(int $quantity): int
{
if ($quantity < 1) {
throw new InvalidArgumentException('Quantity must be positive.');
}
return $quantity;
}
final class PositiveQuantityTest extends TestCase
{
public function testZeroIsRejected(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Quantity must be positive.');
positiveQuantity(0);
}
}
Set the expectation immediately before the call that should throw. This avoids accidentally satisfying the test with an earlier exception from setup code.
Common Mistakes
- Testing that any exception occurs when the class matters.
- Placing the expected-exception declaration after the throwing call.
- Asserting fragile full messages that are not part of the contract.
- Catching exceptions in production code only to hide them.
What To Practise
- Test expected exception classes.
- Distinguish domain rejection from unexpected failure.
- Keep failure contracts deliberate.
Practice
Practice: Test Quantity Rejection
Write checks for a quantity validator that accepts positive integers and rejects zero or negatives.
Requirements
- Show the successful case.
- Catch and identify the expected exception class.
- Check the stable message.
- Include zero and a negative value.
Show solution
The loop verifies both rejected inputs.
<?php
declare(strict_types=1);
function positiveQuantity(int $quantity): int
{
if ($quantity < 1) {
throw new InvalidArgumentException('Quantity must be positive.');
}
return $quantity;
}
echo positiveQuantity(2) . PHP_EOL;
foreach ([0, -4] as $quantity) {
try {
positiveQuantity($quantity);
} catch (InvalidArgumentException $exception) {
echo $exception->getMessage() . PHP_EOL;
}
}
// Prints:
// 2
// Quantity must be positive.
// Quantity must be positive.
A PHPUnit test would use expectException() before calling the invalid case.