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 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.