TestRunner


First, let's go over the initial implementation of TestRunner.

[View full width]

package sis.testing; import java.util.*; import java.lang.reflect.*; class TestRunner { private Class testClass; private int failed = 0; private Set<Method> testMethods = null; public static void main(String[] args) throws Exception { TestRunner runner = new TestRunner(args[0]); runner.run(); System.out.println( "passed: " + runner.passed() + " failed: " + runner.failed()); if (runner.failed() > 0) System.exit(1); } public TestRunner(Class testClass) { this.testClass = testClass; } public TestRunner(String className) throws Exception { this(Class.forName(className)); } public Set<Method> getTestMethods() { if (testMethods == null) loadTestMethods(); return testMethods; } private void loadTestMethods() { testMethods = new HashSet<Method>(); for (Method method: testClass.getDeclaredMethods()) testMethods.add(method); } public void run() { for (Method method: getTestMethods()) run(method); } private void run(Method method) { try { Object testObject = testClass.newInstance(); method.invoke(testObject, new Object[] {})[2]; } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof AssertionError) System.out.println(cause.getMessage()); else e.printStackTrace(); failed++; } catch (Throwable t) { t.printStackTrace(); failed++; } } public int passed() { return testMethods.size() - failed; } public int failed() { return failed; } }

[2] You can use the slightly-more-succinct idiom new Object[0] in place of new Object[] {}.

If you're having a bit of trouble understanding the run(Method) method, refer to Lesson 12 for a discussion of reflection. The basic flow in the run method is:

  • Create a new instance of the test class. This step assumes a no-argument constructor is available in the test class.

  • invoke the method (passed in as a parameter) using the new test class instance and an empty parameter list.

  • If the invoke message send fails, extract the cause from the thrown InvocationTargetException; the cause should normally be an AssertionError. Java throws an AssertionError when an assert statement fails.

TestRunner supplies two constructors. One takes a Class object and for now will be used from TestRunnerTest only. The second constructor takes a class name String and loads the corresponding class using Class.forName. You call this constructor from the main method, which provides a bit of user interface for displaying test results.

The main method in turn gets the class name from the Ant target:

 <target name="runAllTests" depends="build" description="run all tests">   <java classname="sis.testing.TestRunner" failonerror="true" fork="true">     <classpath ref />     <jvmarg value="-enableassertions"/>     <arg value="sis.testing.TestRunnerTest" />   </java> </target> 

There are a few interesting things in the runAllTests target:

  • You specify failonerror="true" in the java task. If running a Java application returns a nonzero value, Ant considers the execution to have resulted in an error. The build script terminates on an error. Using the System.exit command (see the main method in TestRunner) allows you to terminate an application immediately and return the value passed as a parameter to it.

  • You specify fork="true" in the java task. This means that the Java application executes as a separate process from the Java process in which Ant executes. The pitfall of not forking is that the Ant build process itself will crash if the Java application crashes.

  • You pass the test name to TestRunner using a nested arg element.

  • You pass the argument enableassertions to the Java VM using a nested jvmarg element.



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net