Skip to main content

Small Rules, Big Difference

Good tests follow a few habits:

1. Clear names — testGreetEmpty says what broke without opening the file.
2. One behavior per test — small tests point straight at the problem.
3. Data providers for many cases — one table instead of copied tests.
4. Assertions that explain themselves — assertSame shows expected and actual on failure.
5. Separate methods to see everything, assertions in a row to fail fast.
6. Run tests often — after every small change, not once at the end.
7. Tests live next to the code — same project, Test.php suffix.


Failure Messages That Help

A failing test is read in a moment of confusion — the message must carry the whole story: which call, what came back, what was expected.

assertSame prints the expected and actual values as a diff on its own; the optional message argument adds the missing piece — which call produced the value.

A message with the whole story

greet returns the wrong greeting. The failure shows the custom message naming the call, then the diff: expected 'Hello, Ana!', actual 'Hi Ana' — the bug (wrong word, missing comma and exclamation mark) is visible in the output alone, without opening the code.

Prompt
Write a PHPUnit test file where a greet function returns the wrong greeting ("Hi Ana" instead of "Hello, Ana!") and the test compares with assertSame, adding a message that names the call. Show the failing phpunit output with the expected/actual diff.
Output: PHP script

One Behavior per Test

A test that checks five things has five reasons to fail — and its name can only describe one of them. Splitting by behavior keeps every test small and its name honest.

Here the normal greeting and the empty-name fallback are two behaviors, so they get two test methods. When one fails, its name already tells you which behavior broke.

Two behaviors, two tests

testGreetName checks the normal case, testGreetEmpty the fallback. The --testdox output lists them separately — a failure would point at exactly one behavior.

Prompt
Write a PHPUnit test file with a greet function that greets by name and falls back to "Hello, stranger!" for an empty name. Write two separate focused test methods, one per behavior, with names that say what they check. Show the passing phpunit --testdox output with both tests listed.
Output: PHP script

How Much Is Tested: Coverage

PHPUnit can measure which lines of your code the tests actually execute. It needs a coverage extension in PHP — PCOV or Xdebug — and reports the one it found in its header line.

./vendor/bin/phpunit --coverage-filter . --coverage-text DescribeTest.php measures the files in the current folder and prints the report after the test result.

The number is a compass, not a goal: a low value shows untested paths — here the test only walks the mild branch, so the freezing and hot lines are never executed. 100% coverage does not prove the code is correct, but the uncovered lines are guaranteed unchecked.

Measure the coverage

describeTemperature has three branches, the test exercises one. The report counts every executable line in the file — the test method's own line included — and says Lines: 66.66% (4/6): the two uncovered lines are the freezing and hot returns. Adding tests for the cold and hot branches would drive the number up.

Prompt
Write a PHPUnit test file with a describeTemperature function that returns freezing, mild or hot depending on a temperature, and a single test that only checks the mild branch. Run phpunit with a coverage extension and show the --coverage-text output with the line coverage percentage.
Output: PHP script
Measure the coverage