Unit testing

Unit testing needs to be sorted out before implementing any classes, even if writing actual tests isn't going to start yet. IDEs or unit testing frameworks such as JUnit can be used (and it is a good idea to look into them for ideas, at least). But what is the simplest approach that works properly?

For small classes, testing can be done in a main method in the class itself. Java's assertions could be used, but are complicated by the need for the -ea flag. So it seems simpler to avoid assertions and define classes like this:

class X {
    static void claim(boolean b) { if (!b) throw new Error("Bug"); }
    public static void main(String[] args) {
        claim(2+2 == 4);
        System.out.println("Class X OK");
    }
}

The claim method is roughly equivalent to Java's assert keyword. This is all that is necessary for the current project.

Further information

This simple approach to testing can be developed further in various ways. For example, rather than call claim(s.equals("x")), it might be preferable to use claimEq(s, "x"). The claimEq method can be written:

static void claimEq(Object x, Object y) {
    if (x == y) return;
    if (x != null && x.equals(y)) return;
    throw new Error("Bug");
}

Because of the boxing/unboxing feature of Java, this works for primitive types, e.g. integers and characters, as well as objects which have a suitable equals method. (However, as it stands, it doesn't work for arrays).

If you do want to use Java's assertions, you can test whether they have been switched on like this:

...
public static void main(String[] args) {
    boolean testing = false;
    assert(testing = true);
    if (! testing) System.err.println("You forgot the -ea flag");
    ...
}

Note the argument to the assert call is an assignment testing = true not a comparison testing == true.

The main method approach to unit testing can also be scaled up to large projects, by defining classes something like this:

class X implements Testable {
    public String test(String in) { ... }
    public static void main(String[] args) {
        Test.run(args, new X());
    }
}

The Testable interface describes a test method which takes a test expressed as a string and returns a string result which can be checked against what is expected. The test method may be effectively static, creating any objects it needs, but is not declared static, so that it can be described by the interface.

Shared test code is put into a general Test class, which doesn't depend directly on any of the project classes, only on the Testable interface, so that a class can be tested when other classes that it doesn't depend on are broken. The run method has the signature:

static run(String[] args, Testable sample) { ... }

The object passed as the second argument is just a sample object used by the Test class to access the test method. The battery of tests for a class is put into a text file, which is read and interpreted by the Test class.