Calling One Test Case From Another

Moved from MethodsShouldBePublic:

The other day I was thinking about JUnit (JavaUnit). How could I call a test case from another test case and delegate all the error recording to the calling test case? I went through a whole slew of ideas that had no merit. Most of them rather complex things that would have made the framework hairy. One idea was to make a MethodObject for the run () method of TestCase. In a normal test case, run () executes the test in a try block and catches any exceptions that occur. I thought "well, if I could just substitute in something that just executes the test and doesn't catch anything, whoever called the test would be responsible for catching the exceptions." And that was exactly what I wanted. Then it dawned on me. There is a protected TemplateMethod named runTest () which actually runs the test. The run () method sets up the try block and calls runTest (). I can just call runTest!

If the access is changed a test can be called from another test case by doing the following:

new SomeTestCase? (liveObject1, liveObject2).runTest ()

The caller is responsible for catching the exceptions. Moreover, the failures end up in the caller's TestResult. However, if it were public, one could also test live in an application by making a test case which accepts a chunk of the app and executes it in a run () call. The code for the app can have the line above in it to do some test.

Further, one could do a general assertion like this if the assert method was public:

new TestCase ("").assert (liveObject.tag () == 2);

Observation. This may not be possible in Java because exceptions have to be declared. In C++, it should work.

I actually have run into this issue, and came up with a simple solution that does not need to break encapsulation.

I have a base class, TestSuite, which provides error logging, via a reportError protected method.

''This class has two subclasses, TestHarness?, and MultiTestSuite?. The former class uses reflection to run multiple methods in its subclasses, catching any exceptions thrown from them and calling reportFailure (the tests themselves call the same method if they detect a discrepancy). To run multiple suites, you simply subclass MultiTestSuite? and feed it as many TestSuite subclasses as you like. The resulting program tallies all of the error messages and prints a total number of errors detected per suite.''

All of this w/o losing private and protected methods... --RussellGold

This has not been a problem I have encountered in my own test suites. In part, this is because I declare the tests for a class within a static initializer in the class itself; e.g.

class LateForClass? {
static {
new Test("basic arithmetic") {
public/*?*/ boolean run() {
return 3+4 == 7;
}
};// Don't forget this semicolon!
}
}

Since the test appears within the body of the class, I assume it has access to all the elements of LateForClass?.

Test.Test(String) records all the tests that are created, and then the static method Test.main(String[]) enumerates the tests and runs each of them. Test.start() sets up the Throwable handling before invoking Test.run(). I have used a similar setup when the tests were actually invokable from a user interface.

-- BillTrost

In C++ you can make the unit tests a friend of the testee. In Java you can use an inner class for the test. In Eiffel you can use selective export. All of these are arguably better than the static block that BillTrost describes, because they generate separate test modules/.class files that can be left out of a release build (the static blocks actually result in a whole slew of little anonymous inner classes -- although toasting the right ones can be challenging -- BT). This avoids code bloat, and means you're not so scared of adding very large test methods. -- DaveHarris


EditText of this page (last edited March 19, 2004) or FindPage with title or text search