In the design recipe given earlier in this section, we suggested including sample instances for each concrete class in a data definition as static final fields of the respective classes. We could add the following static final fields in the classes Empty and Cons to perform this function.
... class Empty extends IntList { ... static final ONLY = new Empty(); } class Cons extends IntList { ... static final oneElt = new Cons(1, Empty.ONLY); static final twoElts = new Cons(5, oneElt); static final threeElts = new Cons(-10, twoElts); }
Although this approach works well for small programs, it does not scale well to larger programs. The storage overhead involved in storing sample data values as static members of program classes may be significant. Moreover, when testing programs that mutate data, the testing process may destructively modify the test inputs so they must be re-constructed before each test run. A much more robust approach to writing tests is to define a separate test class CTest for each target class (or class hierarchy) C that we want to test. This approach is used in the widely used JUnit test framework, which we will discuss later in the monograph.
For now, we will write our test code in a separate test class without the support of the JUnit test framework. For simple target classes like IntList, we will define our test inputs as static final fields of the test class.