Test for fun and profit, Part 2: Unit testing
Learn the ins and outs of testing in a Java environment
By Todd Sundsted, JavaWorld.com, 07/26/01
Page 2 of 3
As an aside, we'll see that if we modify this simple notion slightly and examine the changes in the box's internal state,
we can greatly simplify the test development for units whose internal states are well reflected in the values of their outputs.
Inputs and outputs
In Java, the principal vehicle by which we apply inputs and obtain outputs is the method invocation. After all, we're ultimately
interested in testing a unit's behavior, and methods contain the code that makes the unit do the behaving.
Two ways to apply inputs
The code above illustrates the two primary ways that inputs are applied during a method invocation. Let's consider each in
turn.
- A method's parameter list provides the most obvious input path:
public
void
add(Node node) {
.
.
.
- The invocation of a method on an external object provides the other input path. I'll explain what an external object is shortly.
What's important is that a method invocation introduces information that can affect the functioning of the unit being tested
and therefore qualifies as an input:
Handle handle = _service.register(node);
Four ways to obtain outputs
The code for a single class also illustrates the four primary ways outputs are obtained as the result of a method invocation.
Let's consider each in turn.
- The most common way to return an output from a method is to supply a return value:
- The second way to provide output is through the parameter list. When a method is invoked, the caller supplies it with parameters
as inputs. Many times the method leaves the parameters unchanged (for primitive types, which are passed by value, this is
always the case). But a method may change the parameters if they permit it -- if they are not immutable (such as an instance
of the
String class, for example):
stringbuffer.append(stringPrefix);
- The third way to provide output from a method is through the exception mechanism:
throws EvaluationException...
- Finally, the forth way to provide output is through method calls made on an external object:
Handle handle = _service.register(node);
Internal and external objects considered
You'll notice that in the discussion above I use the term external object. Let's consider the difference between internal and external objects in a little more detail since it affects testing significantly;
whether an object is internal or external can also affect the amount of work required to design a suite of tests. The line
between internal and external objects is not always well defined, but qualitatively it amounts to the following:
- An internal object is an entity whose entire scope of effect is constrained by the enclosing object. A private member variable containing the
name of an instance, for example, is an internal object. An object's methods can manipulate the internal object and can call
methods on the internal object, but neither of those operations exerts any effect on the environment outside the enclosing
object. Internal objects are sometimes said to compose the enclosing object.
- An external object is an entity whose entire scope of effect is not completely constrained by the enclosing object. This set typically includes
objects that were passed in during construction or initialization and that are held in member variables. Classes with static
methods also fall within the scope of this definition.
The effects that external objects will have as inputs and outputs must be taken into account when you develop tests.