CS101: Documentation, Testing, and Debugging
Copyright © 1996-97
Kenneth J. Goldman
Documentation is textual information within a program
that helps convey the meaning of the program to human readers
who need to understand, modify, or debug the program.
Forms of Documentation
(See also the CS101 Style
Guide.)
- Inherent documentation -- "self-documenting" code is the
best form of documentation. Carefully chosen names for
variables, procedures, etc. are extremely important in
conveying their purpose. Code should be presented in a
logical, easy-to-follow manner, conforming to standard
conventions of order and indentation.
- Inline documentation -- occasional comments within the
code, usually to the right, that explain the purpose of that
line of code. Sometimes, a variable or parameter needs more
explanation, or the purpose of a procedure is not
immediately obvious from the name. Assumptions must be
included. (In Java, everything from a
//
until the end of a line is considered a comment, and the
compiler ignores it.)
- Header comments -- a paragraph at the top of a file that
explains the purpose of its contents. This is useful for
someone unfamiliar with the program who needs an overview.
- External documentation -- information for programmers
wishing to incorporate your code into a larger project. The
functionality of each piece is explained, but the internal
implementation details are not.
- User manual -- information for users of the application on
how to use the features of the program.
In CS101, we will concentrate on the first 3 forms of documentation.
You will see examples of external documentation in CS101, and may
begin writing some of your own external documentation in CS102.
All of your code in CS101 should conform to the
CS101 Style Guide.
Principles of Testing
Testing is a comprehensive organized effort to exercise all
aspects of a software system for possible failure.
- Successful testing is not a proof of correctness (unless
you try every input exhaustively), but it can provide useful
reassurance.
- Design test cases. Testing should not be "random."
- When the number of possible inputs is small, try them
all.
- Test all "branches" of the code. For example, be sure
to include test cases that go through the
if
part as well as the else
part of a conditional statement.
- Test boundary cases, like 0, 1, -1, etc.
- Look for ways to "break" the program, and try those to
be sure they don't.
- Know what the expected results of your tests should be.
Otherwise, you might not recognize an incorrect result.
- Always be aggressive about testing. If you test gingerly,
you won't uncover as many errors. Remember, the objective
in testing is to find errors.
- Save your test program (and the expected results) so you
can run it again later. Whenever modifying a program to fix
one problem, there is a risk that you might have caused a
different problem, so it's best to repeat all the tests.
Taxonomy of Program Errors
- Compilation Errors
- Syntax errors -- The compiler cannot understand your
program because it does not follow the Java
syntax (the rules for a valid program). Common
syntax errors include
- missing or misplaced
;
or
}
,
- missing return type for a procedure,
- missing or duplicate variable declaration.
- Type errors -- these include
- type mismatch on assignment,
- type mismatch between actual and formal parameters
(the Java compiler might say "no such method ..."
instead).
- Run-time Errors
- Output errors -- the program runs but produces an
incorrect result. This indicates an error in the
meaning of the program (logic error).
- Exceptions -- the program terminates abnormally.
Examples include
- division by zero,
- null pointer (we'll see what this means later),
- out of memory.
These also indicate an error in the semantics (logic
error).
- Non-termination -- the program does not terminate as
expected, but continues running "forever." (We'll see
how this could happen later in the course.)
Correcting Compiler
Errors
The compiler reports the approximate location of the error and a
vague description of it. In Emacs, use the middle mouse
button (if there's no middle mouse button, click both mouse
buttons simultaneously) to click on the highlighted portion of the
error message to find that location in the code. Then study
that line, as well as a few previous lines, for the error.
Check any procedure call on that line for proper number, order,
and types of arguments.
Note: Errors may cause the compiler to go "off course." Try
correcting the first few errors and running the compiler
again.
Correcting Run-time Errors
(Debugging)
- Avoid it. Taking the time to write a correct and
well-documented program from the outset will pay back many
times over. Finding and correcting bugs takes a lot of
time, and it can be frustrating, especially if documentation
is insufficient. However, nobody writes perfect software
all the time.
- Debug on a small scale. Thoroughly test each component of
an application (each method of each object) piece by piece,
correcting errors as you go. This is much
easier than finding errors in a large program.
- Understand the symptom. Before changing a program,
understand the error and where in the program it occurred.
Look at what the program tried to do and where it went
wrong.
- Reason about each error, trying to understand how it could
have arisen. This is an important problem-solving task.
Form several hypotheses about how the error might have
arisen and explore them.
- Find the source of the error. Don't waste time making
"random" changes to your program. After reasoning about the
error, you can start searching back through the execution of
your program to see where things went wrong. A
debugger can help.
- Examine the call stack and the values of parameters
and local variables. See if it matches your expectation
of what the program "should" do.
- Trace through an execution of your program by
hand and with a debugger, stopping at certain
points in the execution and/or printing out values along
the way.
- Correct the error and test thoroughly.
Using a Debugger
Debugging is best when you don't have to do it, but knowing how
to use a debugging tool can make the debugging process a lot
less painful. In CS101, we will use the debugger built into
the Emacs JDE, but most debuggers provide similar
functionality. However, try to think first before
entering the debugger.