Why software tests need concrete failure cases
Tests are meant to prove that something works. In fact, a test only proves that it passes — and that is not the same thing. This text explains the difference and what follows from it.
What you will learn
A green test only proves itself
A test is a small program that calls another program and compares the result with an expectation. If it passes, that means: in this one case, what came out was what the test expected.
An uncomfortable possibility follows from this: a test can be green because it checks nothing. It calls something, expects nothing concrete, and stays green even when the code under test no longer exists.
// Proves nothing: only checks that nothing was thrown
public function testBerechnung(): void
{
$ergebnis = $this->rechner->brutto(1000);
self::assertNotNull($ergebnis);
}
// Proves something: states input and expected result
public function testBruttoMitRegelsatz(): void
{
self::assertSame(1190, $this->rechner->brutto(1000));
}
// Proves the failure case: this is where the real value lies
public function testNegativerBetragWirdAbgewiesen(): void
{
$this->expectException(InvalidArgumentException::class);
$this->rechner->brutto(-1);
}The counter-check is part of it
A new test for a bug that has been found only counts as one if it fails WITHOUT the fix. That sounds obvious, but it is skipped regularly — and then you have a test that would not have noticed the bug.
We therefore do this explicitly: revert the fix, run the test, see red, reapply the fix, see green. Only then does it count. The same applies to tests with placeholders: if the placeholder dictates the result that is supposed to be checked, the test checks the placeholder.
Which cases are worth it
What is rarely worth it: tests for code without decisions, such as pure passing through of values. There the test costs maintenance and finds nothing.
An example from practice
What a coverage figure does not tell you
"85 percent test coverage" means: 85 percent of the lines of code were executed once during the tests. It does not mean that their behaviour was verified. A series of tests without real expectations produces high coverage and finds nothing.
The better question for a project is therefore not "how high is the coverage" but: for the three rules that would hurt most in operation, is there a test each with input and expected result — and do these tests fail if you deliberately break the rule?
Common misconceptions
About the author
Fabian Lücker is the managing partner of PHP-Schmiede KG in Rheine and has worked in software development since 2007. He is responsible for development and concept design, and here he writes about decisions that arise in real projects.