1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package com.gargoylesoftware.base.testing;
39
40 import com.gargoylesoftware.base.util.DetailedIllegalArgumentException;
41 import java.lang.reflect.Constructor;
42 import java.lang.reflect.InvocationTargetException;
43 import java.lang.reflect.Method;
44 import java.util.HashMap;
45 import java.util.Iterator;
46 import java.util.Map;
47 import junit.framework.TestCase;
48 import junit.framework.TestSuite;
49
50 /***
51 * This class allows you to specify the order that test methods will execute in
52 * and yet still retain the ability to have test methods found via reflection.<p>
53 *
54 * Generally, when you want to specify the order that tests will be run, you write
55 * code that looks like this.
56 * <pre>
57 * public static Test suite() {
58 * final TestSuite suite = new TestSuite();
59 * suite.addTest( new FooTest("testOne") );
60 * suite.addTest( new FooTest("testTwo") );
61 * return suite;
62 * }
63 * </pre>
64 * The problem with this approach is that when testThree() is written, if it isn't
65 * added to the suite method, it won't get executed.<p>
66 *
67 * Using OrderedTestSuite, you write code like this:
68 * <pre>
69 * public static Test suite() {
70 * return new OrderedTestSuite(FooTest.class, new String[]{"testOne", "testTwo"});
71 * }
72 * </pre>
73 * testOne() and testTwo() will be executed in order just as in the first example. The
74 * difference is that OrderedTestSuite will use reflection to find other methods starting
75 * with test and will execute them. This means that if you write testThree but forget to
76 * add it to suite() then it will still get executed, albeit not in a defined order.
77 *
78 * @version $Revision: 1.4 $
79 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
80 */
81 public final class OrderedTestSuite extends TestSuite {
82 private final Class classToTest_;
83 private final String[] orderedMethodNames_;
84
85
86 /***
87 * Create an instance of this test suite
88 *
89 * @param clazz the class that we will use to get the individual tests.
90 * This must be a subclass of TestCase
91 * @param methodNames The names of any methods that must be tested in order
92 */
93 public OrderedTestSuite( final Class clazz, final String[] methodNames ) {
94 if( clazz == null ) {
95 throw new NullPointerException( "clazz" );
96 }
97 if( methodNames == null ) {
98 throw new NullPointerException( "methodNames" );
99 }
100 if( TestCase.class.isAssignableFrom( clazz ) == false ) {
101 throw new DetailedIllegalArgumentException(
102 "clazz", clazz, "Must be a subclass of TestCase" );
103 }
104 classToTest_ = clazz;
105 orderedMethodNames_ = methodNames;
106
107 addMethodsInOrder( getMatchingMethods() );
108 }
109
110
111 private Map getMatchingMethods() {
112 final Map matchingMethods = new HashMap( 89 );
113
114 final Method allMethods[] = classToTest_.getMethods();
115
116 int i;
117 for( i = 0; i < allMethods.length; i++ ) {
118 if( isMatchingMethod( allMethods[i] ) ) {
119 matchingMethods.put( allMethods[i].getName(), allMethods[i] );
120 }
121 }
122
123 return matchingMethods;
124 }
125
126
127 private boolean isMatchingMethod( final Method method ) {
128 final String methodName = method.getName();
129
130 if( method.getParameterTypes().length != 0 ) {
131 return false;
132 }
133
134 if( method.getReturnType() != void.class ) {
135 return false;
136 }
137
138 if( methodName.startsWith( "test" ) ) {
139 return true;
140 }
141
142 int i;
143 for( i = 0; i < orderedMethodNames_.length; i++ ) {
144 if( methodName.equals( orderedMethodNames_[i] ) ) {
145 return true;
146 }
147 }
148 return false;
149 }
150
151
152 private void addTest( final Method method ) {
153 try {
154 final Constructor constructor = classToTest_.getConstructor( new Class[]{String.class} );
155 final TestCase test = (TestCase)constructor.newInstance( new Object[]{method.getName()} );
156
157 addTest( test );
158 }
159 catch( final NoSuchMethodException e ) {
160 throw new TestInitializationFailedException(
161 "Unable to find a constructor that takes a string", e );
162 }
163 catch( final InstantiationException e ) {
164 throw new TestInitializationFailedException(
165 "Unable to instantiate test for method " + method.getName(), e );
166 }
167 catch( final IllegalAccessException e ) {
168 throw new TestInitializationFailedException(
169 "Unable to instantiate test for method " + method.getName(), e );
170 }
171 catch( final InvocationTargetException e ) {
172 throw new TestInitializationFailedException(
173 "Unable to instantiate test for method" + method.getName(), e.getTargetException() );
174 }
175 }
176
177
178 private void addMethodsInOrder( final Map methodMap ) {
179 int i;
180 String name;
181 Method method;
182
183 for( i = 0; i < orderedMethodNames_.length; i++ ) {
184 name = orderedMethodNames_[i];
185
186 method = (Method)methodMap.get( name );
187 methodMap.remove( name );
188 if( method == null ) {
189 throw new TestInitializationFailedException( "method not found [" + name + "]" );
190 }
191 addTest( method );
192 }
193
194 final Iterator iterator = methodMap.values().iterator();
195 while( iterator.hasNext() ) {
196 method = (Method)iterator.next();
197 addTest( method );
198 }
199 }
200 }
201