Let's test drive an even number function:
Hardcode the return value:
The test passes and the code disrespects the argument.
It's a tautology.
... And that's good. If we expect new outcomes given new input we have to say so in a test. Otherwise, our tests may pass while our code breaks. Assumptions need to be validated often.
Pretend we wrote this code to make our lonesome test pass:
This is worse than before.
Six months from now, the name of this function has changed and new people are maintaining the code. It looks very different, to the point where its original purpose is no longer obvious. Its name isn't even? anymore and it's 20 lines long now.
Developer Diane comes along with the intention of adding a new feature to the app. She changes what used to be named our even? function to have this line at the bottom:
She runs the tests and they all pass. However, back when we added our feature without properly driving it out with tests, we added code contingent upon the assumption that even? also returned false when passed an odd number:
We wrote code dependent upon an untested assumption and Diane was able to invalidate it without causing any tests to fail even though she properly test drove her feature. Now every customer qualifies for a refund and it's our fault.
We should not be able to delete any code without breaking a test. If we can delete code without breaking a test, that code is the embodiment of untested assumptions. It may not even be used, increasing the ramp up cost for new team members and maintenance overhead for everyone else.
When we last left our code, we were here:
Now let's write our other, long awaited test:
Only now do we need to make the return value of even? contingent upon its argument.
Only now should we make the return value of even? contingent upon its argument.
Now we're done!
Validate assumptions early and often.