Exploration Through ExampleExample-driven development, Agile testing, context-driven testing, Agile programming, Ruby, and other things of interest to Brian Marick
|
Wed, 12 Apr 2006I'm not one to quibble over definitions. If someone points at something that's obviously a cow and says "deer", I usually don't argue the point. While we're arguing about what it is we're about to feed, the poor beast will starve. Still, it creeps me out when people refer to tests (aka examples) as specifications. There's an important distinction: A specification describes a correct program, while a test provokes a correct program. In math geek terms, specifications are universally quantified statements, ones of the form "for all inputs such that <something> is true of them, <something else> is true of the output." Tests are constant statements, ones with no variables.* They look like this: "given input 5, the output is 87." This matters because, while both kinds of statements can be true or false, the only way to deduce the truth of a universally quantified statement from a set of constant statements is to exhaustively list all possible inputs. That's rarely possible. To make the point concrete, a set of tests allows the programmer to write this: if (the input is that of test 1) Given that code, the tests say absolutely nothing about the correctness of the something that's done for all remaining cases. Absurd example? An employee of a beltway bandit once told me his project had done exactly that. Proudly told me, no less. But let's pretend we live in an ethical culture. There, the tests combine with certain habits and memories to provoke particular actions. Consider a programmer faced with two tests: assert_equal(1, f(1)) Those tests could be passed with this code: def f(x)
But a programmer who's been raised well has a fastidious distaste
for the def f(x)
The assertions themselves are like two pebbles rolling
downhill. Whether they start an avalanche depends on what they roll
into: the hill has to be ready. For the test-driven, the avalanche is
a procedural assurance that the program computes That's why I don't like calling sets of tests a specification. In practical terms, I don't like it because it always, always, always leads to someone making the argument about universal quantification vs. tests or quoting Dijkstra to the effect that "program testing can be used very effectively to show the presence of bugs but never to show their absence." The ensuing discussion is rarely, in my opinion, a good use of time. So what am I doing having it?
* Actually, a test statement can be seen as having variables, being a quantified statement like this: For all a, b, c, ..., x, y, z: given input 5, the output is 87 where each of the variables is something you hope is irrelevant to the output. The trick is to capture all the relevant variables, pin them down, and feed them into the process. |
|