PHP unit tests with closures
A unit test checks one behaviour of a small piece of code. PHPUnit and Pest are the standard frameworks, but a test is simply a function that calls the code and compares the result. Storing the tests in an array of name => closure lets a loop run them all and report each result, so a failing test does not stop the others.
PHP
<?php
class AssertionFailure extends Exception {}
function assertSame(mixed $expected, mixed $actual): void
{
if ($expected !== $actual) {
throw new AssertionFailure("expected " . var_export($expected, true) . " but got " . var_export($actual, true));
}
}
function assertThrows(string $class, callable $code): void
{
try {
$code();
} catch (Throwable $e) {
if ($e instanceof $class) {
return;
}
throw new AssertionFailure("expected $class but got " . get_class($e));
}
throw new AssertionFailure("expected $class but nothing was thrown");
}
function clamp(int $value, int $low, int $high): int
{
if ($low > $high) {
throw new InvalidArgumentException("low > high");
}
return max($low, min($high, $value));
}
$tests = [
"inside the range" => fn() => assertSame(5, clamp(5, 0, 10)),
"below the range" => fn() => assertSame(0, clamp(-3, 0, 10)),
"above the range" => fn() => assertSame(10, clamp(99, 0, 10)),
"bad bounds throw" => fn() => assertThrows(InvalidArgumentException::class, fn() => clamp(1, 5, 2)),
"division by zero" => fn() => assertThrows(DivisionByZeroError::class, fn() => intdiv(1, 0)),
"a failing check" => fn() => assertSame(6, clamp(7, 0, 5)),
];
$failed = 0;
foreach ($tests as $name => $test) {
try {
$test();
echo "PASS $name\n";
} catch (AssertionFailure $e) {
$failed++;
echo "FAIL $name: ", $e->getMessage(), "\n";
}
}
echo count($tests) - $failed, " passed, $failed failed\n";