Proofread, test and debug
Each of these can happen at a number of scales: a whole deliverable
program, a development version of a program, a class or collection of
closely-related classes, a method or function. The specific meaning of
each step varies depending on the scale, but it should be pointed out
that specification includes test cases. In other words,
"specifying what the software is supposed to do" includes writing down
precise test cases (usually in real, compilable syntax) along with what
results you expect from each test case. This has several advantages:
- it forces you to think about how the software will be invoked at
an early stage, so if the interface is hard to use, you'll
discover that now rather than after you've spent a lot of
hours implementing it;
- it forces you to think about possible special cases and their
"right answers" before getting confused by real code;
- it removes an excuse for not testing your program properly
once it's written, because the test cases are already written.
The following illustrates how this applies at several levels of an
object-oriented, user-interactive program.
-
Design the program and specify how it will be tested
- Write sample scenarios showing how user will interact with program.
Write down what you expect to happen in each case.
- List the important services the program needs to provide.
- Diagram which services depend on which others.
- Outline a sequence of versions the program will go through.
Make sure that each version implements a collection of
services that don't depend on others that haven't yet been
implemented, and that each version can be tested before
going on to the next.
Schedule a time for each version to be completed.
-
For each version in turn...
-
Design the version and specify how it will be tested
- Specify what functions will be implemented in this version.
- Write down a collection of test cases to confirm that all the
functions in this version actually work.
- Choose the important classes of objects and diagram their
relationships.
-
For each class (or each closely-related group of classes,
like List, Cons, and Empty)...
-
Design the class and specify how it will be tested
- Describe clearly in English what the class is supposed to represent.
- Write one or more sample sequences of events that might happen to an
object of the class, together with what should happen as a result.
- List the services and behaviors the class should provide for
other objects.
- Write testbed code to test the sequence(s) of events.
- Write skeleton code for the class.
-
For each method...
-
Design the method and specify how it will be tested
- Write a contract specifying what type(s) of data the method
takes and what type(s) of data it returns
- Write several examples of how it would be called, together
with what should happen in each case
-
Write code for the method.
- Write a header (aka skeleton code)
- Write a template for the body (whatever you can fill in
simply based on knowing the input and output types)
- Write the details of the body, which may involve using
design recipes for loops, recursive methods, etc.
-
Test and debug the method
Try each of your examples and make sure they do what they
should.
-
Test and debug the class
Run your testbed code (as well
as any other test cases you've developed while writing the class),
and make sure they do what they should.
-
Test and debug the version
Try the test cases for this version, as well as any test cases you
came up with while writing individual classes,
to make sure all the functions intended for this version work correctly.
Also do regression
testing: try all the test cases for previous versions, to make
sure that you haven't broken something that worked before.
-
Test and debug the program
Try the sample scenarios (as well as any other scenarios you came
up with along the way) and make sure they do what they should.
Last modified:
Mon Aug 25 14:00:14 EDT 2003
Stephen Bloch / sbloch@boethius.adelphi.edu