exercises and solutions
Testing exercises
Testing exercises should ask what behaviour matters, which boundary is involved, and which check gives useful confidence. Unit tests fit deterministic functions; repositories, HTTP clients, queues, and controllers often need integration-level checks.
Practical Example
<?php
$cases = [
['input' => 1250, 'expected' => '12.50'],
['input' => 500, 'expected' => '5.00'],
];
foreach ($cases as $case) {
echo $case['input'] . ' -> ' . $case['expected'] . PHP_EOL;
}
// Prints:
// 1250 -> 12.50
// 500 -> 5.00
Good tests describe behaviour, not implementation trivia. Start with small deterministic functions, then add integration tests for database or HTTP boundaries where defects are more expensive.
Do not chase coverage numbers without considering risk. Add failure and edge cases that could hurt users, data integrity, or deployment confidence.
Practice
Choose Checks For Three Boundaries
Choose suitable checks for a pure money-formatting function, a PDO product repository, and a login form. Explain why one test style is insufficient.
Show solution
Use fast unit cases for deterministic formatting, an integration test against a test database for repository SQL and mapping, and an HTTP or browser-level check for the login journey.
Each check proves a different boundary. Mocking every dependency would miss configuration, SQL, routing, and rendering defects.
Test A Rejected Login
Choose useful checks for a login attempt with a correct email and wrong password. Include the HTTP response, session state, log safety, and rate-limit behaviour.
Show solution
Send the request through the HTTP boundary and assert the controlled rejection response. Confirm that no authenticated session is created, the password is not logged, and repeated failures reach the intended rate-limit path.
A unit test for one password helper is useful but cannot prove the whole login boundary.