This is built on the idea that a program is usually something that takes input, does some computation, and produces output:
A schematic looks like this:
Here input could be from a user typing at the keyboard, from a file, or from any other source. Output is in our case going to be limited to printing things on the screen or to a file, but there are other output devices (such as a printer).
The idea here is that our job is to write a correct program -- that is, a program that gives a specific output when we give it a specific input. Now if we want to be absolutely sure that the program does what it is supposed to do, we should check every single possible input, and make sure that it gives the correct output. However, this is impractical, and even impossible in some cases. For example, imagine a simple program that reads a string of input from the keyboard and prints the string to the display. There are an infinite number of strings that we could type in, and therefore if we were to verify that each one is printed the same as it was typed in would take forever. What we really want to do is to reason about our program as much as possible, and to give it certain well-chosen 'test' inputs rather than try it on all possible inputs.
The reasoning about the program involves knowing what Java statements do, and drawing conclusions from this knowledge as to how the program behaves. In this way we can show that a program behaves in a certain manner for even an infinite number of inputs. This reasoning is somewhat difficult though; in fact as was mentioned before it is impossible to automate this reasoning process for any arbitrary program. Our solution to this problem is therefore to reason as much as possible, and carefully test the program.
As we mentioned in the introduction to this section, we want to structure our program so that it is made up of a number of small methods. If we do so, then we are better able to verify its correctness. The idea here is that if we can verify the correctness of all the small parts (methods, for example) of a program, then this will lead to the correctness of the program as a whole.
Verifying a method is similar conceptually to verifying the program:
A schematic looks like this:
Here we can think of the 'input' to the method as being the object (or in the case of a static method, the static variables). The method does some computation using these values, and the computation can change the values of the object or static variables and the method may give a return value. To verify the method, we want to be able to say that the method makes the correct changes to values of variables and gives the correct return values for all possible states of the object and arguments we might give the method.
Given that we usually can't test all possible inputs to a program or method, we need to pick some inputs, run the program/method on those inputs, and verify that the output is what it is supposed to be. But how do we pick the possible input values out of the possibly infinite number of input values?
We want to use the following guidelines for choosing test cases:
One way of testing methods is to put a main method in each class which is designed to test the methods. Although we haven't talked about this, there is nothing in Java that prevents you from doing so. The keyword main is merely the keyword that tells the Java Virtual Machine which line to start executing (the one immediately following the line with main on it). So what happens when there are main methods in more than one class -- which one is chosen for execution? On the command line, we choose the main method we want to execute by specifying the name of the class that contains that main method:
java ClassToBeTestedfor example. Similarly in an IDE one can specify the name of the class in order to select the appropriate main method.