KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > tivano > junit > ParametrizedTestSuite


1 /* $Id: ParametrizedTestSuite.java,v 1.2 2005/01/26 08:28:44 jkjome Exp $ */
2
3 package de.tivano.junit;
4
5 import java.io.IOException JavaDoc;
6 import java.io.InputStream JavaDoc;
7 import java.lang.reflect.Constructor JavaDoc;
8 import java.lang.reflect.Field JavaDoc;
9 import java.lang.reflect.InvocationTargetException JavaDoc;
10 import java.lang.reflect.Method JavaDoc;
11 import java.util.Enumeration JavaDoc;
12 import java.util.Iterator JavaDoc;
13 import java.util.Properties JavaDoc;
14 import java.util.Set JavaDoc;
15 import java.util.TreeSet JavaDoc;
16
17 import junit.framework.AssertionFailedError;
18 import junit.framework.Test;
19 import junit.framework.TestCase;
20 import junit.framework.TestFailure;
21 import junit.framework.TestResult;
22 import junit.framework.TestSuite;
23
24 /**
25  * A unit test suite that executes the same test case for a number of
26  * different sets of data.
27  *
28  * <p>Test data is defined as a standard Java {@link Properties} object, with a
29  * certain convention on the property keys used: Keys are expected to be of
30  * the form "&lt;test name&gt;.&lt;attribute name&gt;", where the test name part
31  * may be any string allowed as part of a property key, and the attribute name
32  * part must be a legal java identifier. The test name part is used to identify
33  * one of the test data definition in the property set (typically, a single
34  * property set contains many test data definitions), and the attribute name
35  * is used to identify a single attribute within a test data definition.</p>
36  *
37  * <p>A test case is parametrized with a given test data definition by setting
38  * the accessible fields of this test case to the values specified by the
39  * corresponding property in the test data definition. A field in the test
40  * case is considered accessible if it either is a public field or if the
41  * test case class defines a public setter method matching the attribute
42  * name.</p>
43  *
44  * <p>If both a public field and a setter method are present for a
45  * given attribute name, the setter method will be preferred. If more than one
46  * matching setter method is found, the test will fail with an appropriate
47  * error message.</p>
48  *
49  * </ol>
50  *
51  * <p>Note that the fields that can be parametrized must have one of the
52  * types listed here:</p>
53  * <ul>
54  * <li>One of the primitive types <code>boolean</code>, <code>character</code>,
55  * <code>byte</code>, <code>short</code> <code>int</code>, <code>long</code>,
56  * <code>float</code> or <code>double</code>.</li>
57  * <li>An object wrapper of a primitive type: {@link Boolean}, {@link Character},
58  * {@link Byte}, {@link Short}, {@link Integer}, {@link Long}, {@link Float} or
59  * {@link Double}</li>
60  * <li>{@link String}</li>
61  * <li>An arbitrary, non-abstract class that defines a public constructor taking
62  * a single <code>String</code> as an argument (e.g. {@link java.io.File}, or
63  * {@link java.net.URL}).
64  * </ul>
65  * <p>The same restrictions hold for the the parameters of setter methods.
66  * In addition, a setter method may only have a single parameter to be used
67  * for test case parametrization.</p>
68  *
69  * <p>Example: The property set
70  * <pre>
71  * Test1.count = 1
72  * Test1.isSpecial = false;
73  * Test1.url = http://www.tivano.de/
74  *
75  * Test2.count = 1
76  * Test2.isSpecial = false;
77  * Test2.url = file:///some/path
78  *
79  * My.Special.Test.count = 3
80  * My.Special.Test.isSpecial = true
81  * My.Special.Test.special = foo, bar, baz
82  * </pre>
83  * defines three test data sets named "Test1", "Test2" and "My.Special.Test". Test1 and
84  * Test2 each define the attributes "count", "isSpecial" and "url", and My.Special.Test defines
85  * the attributes "count", "isSpecial" and "special".</p>
86  * <p>Now, if the test class
87  * <pre>
88  * public class ExampleTest implements Test {
89  * public int count;
90  * public boolean isSpecial;
91  * private URL url;
92  * public void setIsSpecial(boolean value) { ... }
93  * public void setSpecial(String value) { ... }
94  * ....
95  * }
96  * </pre>
97  * is parametrized by Test1, the variable <code>count</code> will be set to 1,
98  * the method <code>setIsSpecial()</code> will be called with the argument
99  * <code>false</code> and the variable <code>url</code> will be set to the equivalent of
100  * <code>new URL("http://www.tivano.de/")</code>. The method
101  * <code>setSpecial()</code> will not be called, as there is no attribute named
102  * "special" in Test1. When the example test is parametrized by My.Special.Test,
103  * however, <code>setSpecial()</code> will be called with the argument
104  * "foo, bar, baz", and the variable <code>url</code> will be left alone.</p>
105  *
106  * <p>Parametrization occurs after the test case constructor has finished
107  * and before the test case is run. This means that you won't see the
108  * parametrized values in the constructor of your test cases, but they will be
109  * available in the <code>run(...)</code> and (for tests inheriting
110  * {@link TestCase}) <code>setUp()</code> methods.</p>
111  *
112  * <p>If a test case cannot be parametrized e.g. because there is no
113  * corresponding public field or setter method for an attribute, the
114  * field or setter method violates the restrictions given above, or the
115  * field or setter method is not public, the test will fail with an
116  * appropriate error message.</p>
117  *
118  * @author Richard Kunze
119  */

120 public class ParametrizedTestSuite extends TestSuite {
121     
122     private Properties JavaDoc TEST_DATA = null;
123
124     /** Helper class for reporting warnings. This test always fails. */
125     static class Warning extends TestCase {
126         private final String JavaDoc MESSAGE;
127         public Warning(String JavaDoc message) {
128             super("warning");
129             MESSAGE = message;
130         }
131         protected void runTest() { fail(MESSAGE); }
132     }
133     
134     /** Helper class for parametrizing a single test case.
135      * The test is parametrized immediately before calling the
136      * <code>run(TestResult)</code> method of the decorated test.
137      */

138     class ParametrizedTestWrapper extends TestCase {
139         final Test WRAPPED;
140         /** Construct a parametrized wrapper for <code>test</code>.
141          * @param key the key prefix for the associated test data set
142          */

143         public ParametrizedTestWrapper(Test test) {
144             super(null);
145             if (test instanceof TestCase)
146                 setName(((TestCase)test).getName());
147             WRAPPED = test;
148         }
149         
150         public Test getTest() { return WRAPPED; }
151         
152         /** Parametrize and run the test */
153         public void run(TestResult result) {
154             result.startTest(this);
155             if (parametrizeTest(result)) {
156                 // Collect the results in a temporary test result. Any errors or
157
// failures are added to the result later, substituting this
158
// object for the wrapped test.
159
TestResult tmp = new TestResult();
160                 WRAPPED.run(tmp);
161                 Enumeration JavaDoc errors = tmp.errors();
162                 while (errors.hasMoreElements()) {
163                     Throwable JavaDoc err = ((TestFailure)errors.nextElement()).thrownException();
164                     result.addError(this, err);
165                 }
166                 Enumeration JavaDoc failures = tmp.failures();
167                 while (failures.hasMoreElements()) {
168                     AssertionFailedError err =
169                        (AssertionFailedError)((TestFailure)failures.nextElement()).thrownException();
170                     result.addFailure(this, err);
171                 }
172             }
173             result.endTest(this);
174         }
175                
176         /** Get the data set key prefix for this test. */
177         private String JavaDoc getKeyPrefix() {
178             return ParametrizedTestSuite.this.getName();
179         }
180         
181         /** Parametrize the decorated test. If there are errors in parametrizing the
182          * test, appropriate failure messages are added to <code>result</code> and
183          * this method returns <code>false</code>.
184          */

185         private boolean parametrizeTest(TestResult result) {
186             boolean success = true;
187             Enumeration JavaDoc keys = TEST_DATA.keys();
188             String JavaDoc keyPrefix = getKeyPrefix() + ".";
189             int prefixLen = keyPrefix.length();
190             Test test = WRAPPED;
191             Class JavaDoc testClass = test.getClass();
192             while (keys.hasMoreElements()) {
193                 String JavaDoc key = keys.nextElement().toString();
194                 // Skip this entry if the key doesn't belong to the data
195
// set for this test case, or if it is too short to contain
196
// an attribute name.
197
if (!key.startsWith(keyPrefix) || key.length() <= prefixLen) {
198                     continue;
199                 }
200                 String JavaDoc attributeName = key.substring(prefixLen);
201                 // Find the approriate field/setter method, and determine the
202
// field/argument type.
203
try {
204                     Method JavaDoc setterMethod =
205                         findSetterMethod(testClass, attributeName);
206                     Field JavaDoc field = null;
207                     if (setterMethod == null)
208                         field = findPublicField(testClass, attributeName);
209                     if (setterMethod == null && field == null) {
210                         fail("Cannot set value for attribute '" + attributeName +
211                               "': No setter method or public member field found " +
212                               "with that name.");
213                     }
214                     Object JavaDoc value;
215                     if (setterMethod != null) {
216                         // Getting the first parameter cannot fail here because
217
// findSetterMethod() already checks that the method has
218
// exactly one parameter.
219
value = parseAttributeValue(attributeName,
220                                                     TEST_DATA.getProperty(key),
221                                                     setterMethod.getParameterTypes()[0]);
222                         
223                         Object JavaDoc[] args = { value };
224                         try {
225                             setterMethod.invoke(test, args);
226                         } catch (InvocationTargetException JavaDoc e) {
227                             Throwable JavaDoc cause = e.getTargetException();
228                             if (cause instanceof RuntimeException JavaDoc) {
229                                 throw (RuntimeException JavaDoc)cause;
230                             } else if (cause instanceof Error JavaDoc) {
231                                 throw (Error JavaDoc)cause;
232                             } else fail("Exception in " + setterMethod.getName() +
233                                         ": " + cause.toString());
234                         } catch (IllegalAccessException JavaDoc e) {
235                             fail("Cannot set value for attribute '" + attributeName +
236                                  "': " + e.toString());
237                         }
238
239                     } else {
240                         value = parseAttributeValue(attributeName,
241                                                     TEST_DATA.getProperty(key),
242                                                     field.getType());
243                         try {
244                             field.set(test, value);
245                         } catch (IllegalAccessException JavaDoc e) {
246                             fail("Cannot set value for attribute '" + attributeName +
247                                  "': " + e.toString());
248                         }
249                     }
250                 } catch (AssertionFailedError e) {
251                     result.addFailure(this, e);
252                     success = false;
253                 }
254             }
255             return success;
256         }
257         
258         /** Find the appropriate setter method for <code>attrName</code>.
259          * If more than one setter method is found or if no matching setter
260          * is found at all, this method throws an <code>AssertionFailedError</code>
261          * with appropriate error message.
262          * @param testClass the test class
263          * @param attrName the name of the attribute to find a setter method for.
264          */

265         private Method JavaDoc findSetterMethod(Class JavaDoc testClass, String JavaDoc attrName) {
266             String JavaDoc methodName = "set" + Character.toUpperCase(attrName.charAt(0))
267                                 + attrName.substring(1);
268             Method JavaDoc[] allMethods = testClass.getMethods();
269             
270             // Find a suitable method. At the same time, count all methods
271
// that are suitable candidates, and check if there are methods
272
// with a matching name at all.
273
Method JavaDoc candidate = null;
274             boolean foundMatchingName = false;
275             int matchingMethodCount = 0;
276             for (int i=0; i<allMethods.length; i++) {
277                 if (!allMethods[i].getName().equals(methodName)) continue;
278                 foundMatchingName = true;
279                 Class JavaDoc[] params = allMethods[i].getParameterTypes();
280                 if (params.length != 1) continue;
281                 if (isValidAttributeType(params[0])) {
282                     candidate = allMethods[i];
283                     matchingMethodCount++;
284                 }
285             }
286             // If there is no method at all with a matching name, return
287
// null so that the caller goes on checking for a public field.
288
if (!foundMatchingName) return null;
289             
290             // OK, we have a matching name. Now check if there is exactly one
291
// method with this name that has a matching signature (i.e., exactly
292
// one argument of a valid type). If this is not hte case, report an
293
// appropriate error.
294
if (matchingMethodCount != 1) {
295                 fail("Cannot set value for attribute '" + attrName + "': "
296                      + (matchingMethodCount==0?"No":"More than one")
297                      + " setter method with compatible parameter types found.");
298             }
299             
300             // Still here? fine, we've found our setter...
301
return candidate;
302         }
303         
304         /** Helper method to find a public field of matching type for
305          * <code>fieldName</code>. If no field with that name is found, this
306          * method returns <code>null</code>. If a field is found but does not
307          * have a valid type, this method throws an <code>AssertionFailedError</code>
308          * with appropriate error message.
309          */

310         private Field JavaDoc findPublicField(Class JavaDoc testClass, String JavaDoc fieldName) {
311             try {
312                 Field JavaDoc field = testClass.getField(fieldName);
313                 if (!isValidAttributeType(field.getType())) {
314                     fail("Cannot set value for attribute '" + fieldName +
315                          "': Public member field has incompatible type '" +
316                          field.getType().getName() + "'");
317                 }
318                 return field;
319             } catch (NoSuchFieldException JavaDoc e) {
320                 return null;
321             }
322         }
323         
324         /** Check if <code>type</code> is a type compatible with parametrization. */
325         private boolean isValidAttributeType(Class JavaDoc type) {
326             if (type.isPrimitive() && !type.equals(Void.TYPE)) return true;
327             if (Boolean JavaDoc.class.equals(type) || Character JavaDoc.class.equals(type)
328                 || Byte JavaDoc.class.equals(type) || Short JavaDoc.class.equals(type)
329                 || Integer JavaDoc.class.equals(type) || Long JavaDoc.class.equals(type)
330                 || Float JavaDoc.class.equals(type) || Double JavaDoc.class.equals(type)
331                 || String JavaDoc.class.equals(type)) return true;
332             
333             // Not a primitve, an object wrapper of a primitive, or a string ->
334
// Check if type has a public constructor taking a string as an
335
// argument.
336
final Class JavaDoc[] ARG_TYPES = { String JavaDoc.class };
337             try {
338                 type.getConstructor(ARG_TYPES);
339                 return true;
340             } catch (NoSuchMethodException JavaDoc e) {
341                 return false;
342             }
343         }
344         
345         /** Helper method to parse a string into an object of the desired type.
346          * For primitive types, this method returns the appropriate object wrapper.
347          * @throws AssertionFailedError if the string cannot be parsed.
348          */

349         private Object JavaDoc parseAttributeValue(String JavaDoc name, String JavaDoc value, Class JavaDoc type) {
350             try {
351                 if (value == null) return value;
352                 if (Boolean.TYPE.equals(type) || Boolean JavaDoc.class.equals(type)) {
353                     return Boolean.valueOf(value);
354                 } else if (Character.TYPE.equals(type) || Character JavaDoc.class.equals(type)) {
355                     if (value.length() != 1) {
356                         fail("Cannot set attribute '" + name + "' to value '" +
357                              value + "': Value too long.");
358                     }
359                     return new Character JavaDoc(value.charAt(0));
360                 } else if (Byte.TYPE.equals(type) || Byte JavaDoc.class.equals(type)) {
361                     return Byte.valueOf(value);
362                 } else if (Short.TYPE.equals(type) || Short JavaDoc.class.equals(type)) {
363                     return Short.valueOf(value);
364                 } else if (Integer.TYPE.equals(type) || Integer JavaDoc.class.equals(type)) {
365                     return Integer.valueOf(value);
366                 } else if (Long.TYPE.equals(type) || Long JavaDoc.class.equals(type)) {
367                     return Long.valueOf(value);
368                 } else if (Float.TYPE.equals(type) || Float JavaDoc.class.equals(type)) {
369                     return Float.valueOf(value);
370                 } else if (Double.TYPE.equals(type) || Double JavaDoc.class.equals(type)) {
371                     return Double.valueOf(value);
372                 } else if (String JavaDoc.class.equals(type)) {
373                     return value;
374                 } else {
375                     final Class JavaDoc[] ARG_TYPES = { String JavaDoc.class };
376                     Constructor JavaDoc c = type.getConstructor(ARG_TYPES);
377                     try {
378                         final Object JavaDoc[] ARG = { value };
379                         return c.newInstance(ARG);
380                     } catch (InvocationTargetException JavaDoc e) {
381                         throw e.getTargetException();
382                     }
383                 }
384             } catch (AssertionFailedError e) {
385                 throw e;
386             } catch (Throwable JavaDoc t) {
387                 fail("Cannot set attribute '" + name + "' to value '" +
388                       value + "': " + t.toString());
389                 // this is only here to keep the compiler happy - fail()
390
// always throws an exception...
391
return null;
392             }
393         }
394         
395         public String JavaDoc toString() {
396             return WRAPPED.toString() + "#" + getKeyPrefix();
397         }
398         
399     }
400         
401     /** Helper method to prepare the test data. This method always returns
402      * a <code>Properties</code> object, even if the argument is <code>null</code>.
403      * In this case, and when the argument is empty, a warning with an appropriate
404      * error message is added to this test suite.
405      * @param rawData the "raw" test data. May be <code>null</code>
406      * @param clone if <code>true</code>, the test data is cloned.
407      * @return a <code>Properties</code> object containing the test data.
408      */

409     private Properties JavaDoc prepareTestData(Properties JavaDoc rawData, boolean clone) {
410         if (rawData == null) {
411             super.addTest(warning("No test data specified for test suite '" + getName() + "'!"));
412             return new Properties JavaDoc();
413         } else if (rawData.isEmpty()) {
414             super.addTest(warning("Test data definition for test suite '" + getName() + "' is empty!"));
415         }
416         if (clone) return (Properties JavaDoc)rawData.clone();
417         else return rawData;
418     }
419     
420     /**
421      * Helper method to find the test data for <code>name</code>. This method tries
422      * to look up a resource named "<em>name</em>.properties" and parse this resource
423      * into a properties object. If this succeeds, the resulting properties object
424      * is returned. Otherwise, this method adds a warning with an appropriate message
425      * to the test suite and returns an empty properties object.
426      * @param name the name of the test data resource
427      */

428     private Properties JavaDoc findTestData(String JavaDoc name) {
429         String JavaDoc resourceName = name + ".properties";
430         InputStream JavaDoc res = getClass().getResourceAsStream(resourceName);
431         
432         Properties JavaDoc prop = new Properties JavaDoc();
433         if (res == null) {
434             super.addTest(warning("Cannot load test data: Resource '"
435                                   + resourceName + "' not found!"));
436         } else {
437             try {
438                 prop.load(res);
439             } catch (IOException JavaDoc e) {
440                 super.addTest(warning("Cannot load test data from resource '"
441                               + resourceName +"': " + e.toString()));
442             }
443         }
444         return prepareTestData(prop, false);
445     }
446     
447     /**
448      * Helper method to find the test data for <code>testClass</code>. This method tries
449      * to look up a resource with a name corresponding to the name of the test class with
450      * all occurences of "." replaced by "/", a prefix of "/" and a suffix of ".propertes".
451      * If this succeeds, the resulting properties object is returned. Otherwise, this method
452      * adds a warning with an appropriate message to the test suite and returns an empty
453      * properties object.
454      * @param testClass the class to find test data for
455      */

456     private Properties JavaDoc findTestData(Class JavaDoc testClass) {
457         return findTestData("/" + testClass.getName().replace('.', '/'));
458     }
459     
460     /** Construct an empty, parametrized test suite for the given test data set.
461      * All tests that are subsequently added to this suite using the
462      * {@link #addTest} or {@link #addTestSuite} methods will be parametrized
463      * according to <code>testData</code>.
464      * @param testData the property set defining the test data.
465      */

466     public ParametrizedTestSuite(Properties JavaDoc testData) {
467         this(null, testData);
468     }
469     
470     /** Construct an empty test suite named <code>name</code> for the
471      * test data set <code>testData</code>.
472      * All tests that are subsequently added to this suite using the
473      * {@link #addTest} or {@link #addTestSuite} methods will be parametrized
474      * according to <code>testData</code>.
475      * @param the name for this test suite.
476      * @param testData the property set defining the test data.
477      */

478     public ParametrizedTestSuite(String JavaDoc name, Properties JavaDoc testData) {
479         super(name);
480         TEST_DATA = prepareTestData(testData, true);
481     }
482     
483     /** Construct an empty, parametrized test suite named <code>name</code>.
484      * <p>The test data for this test suite is expected to reside in a resource
485      * named "<em>name</em>.properties". If no such resource exists, the resource
486      * cannot be parsed into a properties object, or the corresponding properties
487      * object is empty, a warning with an appropriate error message is added
488      * to this test suite.</p>
489      * @param name the name for this test suite
490      */

491     public ParametrizedTestSuite(String JavaDoc name) {
492         super(name);
493         TEST_DATA = findTestData(name);
494     }
495     
496     /** Construct a parametrized test suite for a given test class.
497      * <p>The test data for this suite is expected to reside in a resource
498      * with a name matching that of the test class (in detail: The resource
499      * name is constructed by replacing all occurrences of "." in the class name
500      * by "/" and appending the suffix ".properties"), and it will contain tests
501      * for all test methods defined by <code>testClass</code> and all test data sets
502      * specified in the associated test data resource.</p>
503      *
504      * <p>Using this constructor is the exact equivalent of the code sequence
505      * <pre>
506      * String name = "/" + testClass.getName().replace(".", "/") + ".properties"
507      * TestSuite suite = new ParametrizedTestSuite(name);
508      * suite.addTestSuite(testClass);
509      * </pre>
510      * </p>
511      *
512      * <p>See the documentation for {@link #addTestSuite} for an exact definition
513      * of the tests that are constructed by using this constructor.</p>
514      *
515      * <p>If the resource defining the test data set for the test class cannot
516      * be found, cannot be parsed as a property set or if the resulting property
517      * set is empty, a warning will be added to this suite instead of the
518      * actual test. Likewise, a warning will be added if one of the resulting
519      * tests cannot be parametrized (e.g because of a mismatch of the test data
520      * and the fields respectively setter methods defined by the test class).</p>
521      * @param testClass the test class.
522      */

523     public ParametrizedTestSuite(Class JavaDoc testClass) {
524         super(testClass.getName());
525         TEST_DATA = findTestData(testClass);
526         if (!TEST_DATA.isEmpty()) {
527             addTestSuite(testClass);
528         }
529     }
530     
531     /**
532      * Special constructor for {@link #addTestSuite}. The constructed test suite
533      * will hold tests for all test methods in <code>testClass</code>, parametrized
534      * according to the test data set named <code>name</code> defined in
535      * <code>testData</code>. This constructor assumes that <code>testData</code>
536      * is not null or empty and that <code>name</code> is a valid key prefix for
537      * a test data set defined in <code>testData</code>.
538      */

539     private ParametrizedTestSuite(Class JavaDoc testClass, String JavaDoc name, Properties JavaDoc testData) {
540         // Implementation note: The current implementation assumes that the
541
// TestSuite(Class) constructor calls addTest() for every test it
542
// extracts from testClass. Since addTest() wraps its argument in an
543
// ParametrizedTestWrapper if this argument is not a warning,
544
// simply calling super(testClass) is enough to construct the complete suite.
545
// We need to re-set the name, though, because the name is used by the
546
// ParametrizedTestWrapper to look up the appropriate data set, and super(class)
547
// sets the name to be testClass.getName().
548
super(testClass);
549         TEST_DATA = testData;
550         setName(name);
551     }
552     
553     /** Returns a test that will fail with a warning message.
554      * This method must be redefined here for two reasons: First,
555      * it is private in {@link TestSuite} and so not accessible here.
556      * Second, the implementation in <code>TestSuite</code> uses an
557      * anonymous class for the warning, which makes it impossible for
558      * {@link #addTest} to distinguish between warnings that should not
559      * be wrapped in a {@link de.tivano.junit.ParametrizedTestSuite.ParametrizedTestWrapper}
560      * (because doing so will almost certainly cause the wrapper to fail with
561      * a number of spurious errors when it tries to parametrize the warning)
562      * and real tests, which have to be wrapped.
563      * @param message the warning message
564      */

565     protected Test warning(String JavaDoc message) {
566         return new Warning(message);
567     }
568     
569     /** Get the set of key prefixes for all test data sets defined in this test suite.
570      * @return a {@link Set} of {@link Strings}
571      */

572     private Set JavaDoc getTestDataKeys() {
573         // We're using a tree set here to make sure the test case keys are
574
// sorted by name.
575
Set JavaDoc keys = new TreeSet JavaDoc();
576         Enumeration JavaDoc allKeys = TEST_DATA.keys();
577         while (allKeys.hasMoreElements()) {
578             String JavaDoc rawKey = allKeys.nextElement().toString();
579             int endIdx = rawKey.lastIndexOf('.');
580             keys.add((endIdx!=-1?rawKey.substring(0,endIdx):rawKey));
581         }
582         return keys;
583     }
584     
585     /** Add the parametrized test suite for <code>testClass</code> to this suite.
586      *
587      * <p>For every different test data set found in the associated property set,
588      * a test suite will be created and added to this suite that contains tests
589      * for all <code>test...()</code> method found in the test class. Every one of
590      * these tests is parametrized by the corresponding test data set.</p>
591      *
592      * <p>Example: If the class
593      * <pre>
594      * public class ExampleTest extends TestCase {
595      * private String value;
596      * public void setValue(String str) { ... }
597      * public void testFoo() { ... }
598      * public void testBar() { ... }
599      * }
600      * </pre>
601      * is passed as parameter <code>testClass</code> to this method, and the property
602      * set associated with this test suite has the content
603      * <pre>
604      * Test1.value = fnord
605      * Test2.value = snafu
606      * </pre>
607      * then this method will create two test suites named "Test1" and "Test2"
608      * and add them to this suite. Each of the created test suites contains
609      * two instances of ExampleTest: One that runs the <code>testFoo()</code>
610      * test method, and one that runs <code>testBar()</code>. The tests in the
611      * "Test1" suite will have the <code>value</code> attribute set to "fnord",
612      * the tests in the "Test2" suite to "snafu".</p>
613      * @param testClass the test class
614      */

615     public void addTestSuite(Class JavaDoc testClass) {
616         Iterator JavaDoc keys = getTestDataKeys().iterator();
617         while (keys.hasNext()) {
618             // Need to use super.addTest() to prevent addTest() from wrapping
619
// the newly created test suite.
620
super.addTest(new ParametrizedTestSuite(testClass,
621                                                     keys.next().toString(),
622                                                     TEST_DATA));
623         }
624     }
625
626     /** Adds a test to the suite.
627      * <p>If a test data set with a key of <code>this.getName()</code> exists in the test
628      * data associated with this test suite, the test will be parametrized with the
629      * test data in this data set.</p>
630      * <p>Note that if <code>test</code> is an instance of {@link TestSuite}, then it will <em>not</em>
631      * parametrized.</p>
632      * @param test the test to be added to this suite.
633      */

634     public void addTest(Test test) {
635         // Don't try and parametrize warnings - doing so will almost certainly
636
// fail and produce spurious error reports.
637
// Don't parametrize test suites - this doesn't really make sense anyway,
638
// and the interjected test wrapper gets in the way of tools that examine
639
// the test hierarchy (e.g. the junit.swingui.TestRunner).
640
if (test instanceof Warning || test instanceof TestSuite) super.addTest(test);
641         else super.addTest(new ParametrizedTestWrapper(test));
642     }
643 }
644
Popular Tags