Unit testing. Isn't that some annoying requirement that we're going to
ignore? What if I told you we are going to start with unit testing? You ask,
"But how can I test code that hasn't been written?" I reply, "The unit test
will define the code you are going to write."
Many developers get very nervous when you mention unit tests. I suspect they
have this vision of a grand table with every single method listed, along
with the expected results and pass/fail date. I'll leave that kind of unit
testing to folks trying to get FDA approval. It's important, but not
relevant in most programming projects.
The unit test will motivate the code that you write. In a sense, it is a
little design document that says, "What will this bit of code do?" Or, in
the language of object oriented programming, "What will these clusters of
objects do?"
The crucial issue in constructing a unit test is scope. If the scope is too
narrow, then the tests will be trivial and the objects might pass the tests,
but there will be no design of their interactions. Certainly, interactions
of objects are the crux of any object oriented design.
Likewise, if the scope is too broad, then there is a high chance that not
every component of the new code will get tested. The programmer is then
reduced to testing-by-poking-around, which is not an effective test
strategy.
I like to start with a feature that is described in the requirements
document and work down until a method doesn't need a unit test. That way,
tests are grouped according to major feature and should include as many unit
tests that deal with the feature in question as are necessary.
How do you know that a method doesn't need a unit test? First, can it be
tested by inspection? If the code is simple enough that the developer can
just look at it and verify its correctness then it is simple enough to not
require a unit test. The developer should know when this is the case.
Unit tests will most likely be defined at the method level, so the art is to
define the unit test on the methods that cannot be checked by inspection.
Usually this is the case when the method involves a cluster of objects. Unit
tests that isolate clusters of objects for testing are doubly useful,
because they test for failures, and they also identify those segments of
code that are related. People who revisit the code will use the unit tests
to discover which objects are related, or which objects form a cluster.
Hence: Unit tests isolate clusters of objects for future developers.
Another good litmus test is to look at the code and see if it throws an
error or catches an error. If error handling is performed in a method, then
that method can break. Generally, any method that can break is a good
candidate for having a unit test, because it may break at some time, and
then the unit test will be there to help you fix it.
The danger of not implementing a unit test on every method is that the
coverage may be incomplete. Just because we don't test every method
explicitly doesn't mean that methods can get away with not being tested. The
programmer should know that their unit testing is complete when the unit
tests cover at the very least the functional requirements of all the code.
The careful programmer will know that their unit testing is complete when
they have verified that their unit tests cover every cluster of objects that
form their application.
|