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
39
40
41
42
43
44
45
46 package groovy.util;
47
48 import groovy.lang.Closure;
49 import groovy.lang.GroovyRuntimeException;
50 import groovy.lang.GroovyShell;
51
52 import java.util.logging.Logger;
53 import java.lang.reflect.Method;
54 import java.lang.reflect.Modifier;
55
56 import junit.framework.TestCase;
57
58 import org.codehaus.groovy.runtime.InvokerHelper;
59
60 /***
61 * A default JUnit TestCase in Groovy. This provides a number of helper methods
62 * plus avoids the JUnit restriction of requiring all test* methods to be void
63 * return type.
64 *
65 * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
66 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
67 * @author Dierk Koenig (the notYetImplemented feature, changes to shouldFail)
68 * @version $Revision: 1.27 $
69 */
70 public class GroovyTestCase extends TestCase {
71
72 protected static Logger log = Logger.getLogger(GroovyTestCase.class.getName());
73 private static int counter;
74 private boolean useAgileDoxNaming = false;
75
76 public GroovyTestCase() {
77 }
78
79 /***
80 * Overload the getName() method to make the test cases look more like AgileDox
81 * (thanks to Joe Walnes for this tip!)
82 */
83 public String getName() {
84 if (useAgileDoxNaming) {
85 return super.getName().substring(4).replaceAll("([A-Z])", " $1").toLowerCase();
86 }
87 else {
88 return super.getName();
89 }
90 }
91
92 public String getMethodName() {
93 return super.getName();
94 }
95
96 /***
97 * Asserts that the arrays are equivalent and contain the same values
98 *
99 * @param expected
100 * @param value
101 */
102 protected void assertArrayEquals(Object[] expected, Object[] value) {
103 String message =
104 "expected array: " + InvokerHelper.toString(expected) + " value array: " + InvokerHelper.toString(value);
105 assertNotNull(message + ": expected should not be null", expected);
106 assertNotNull(message + ": value should not be null", value);
107 assertEquals(message, expected.length, value.length);
108 for (int i = 0, size = expected.length; i < size; i++) {
109 assertEquals("value[" + i + "] when " + message, expected[i], value[i]);
110 }
111 }
112
113 /***
114 * Asserts that the array of characters has a given length
115 *
116 * @param length expected length
117 * @param array the array
118 */
119 protected void assertLength(int length, char[] array) {
120 assertEquals(length, array.length);
121 }
122
123 /***
124 * Asserts that the array of ints has a given length
125 *
126 * @param length expected length
127 * @param array the array
128 */
129 protected void assertLength(int length, int[] array) {
130 assertEquals(length, array.length);
131 }
132
133 /***
134 * Asserts that the array of objects has a given length
135 *
136 * @param length expected length
137 * @param array the array
138 */
139 protected void assertLength(int length, Object[] array) {
140 assertEquals(length, array.length);
141 }
142
143 /***
144 * Asserts that the array of characters contains a given char
145 *
146 * @param expected expected character to be found
147 * @param array the array
148 */
149 protected void assertContains(char expected, char[] array) {
150 for (int i = 0; i < array.length; ++i) {
151 if (array[i] == expected) {
152 return;
153 }
154 }
155
156 StringBuffer message = new StringBuffer();
157
158 message.append(expected).append(" not in {");
159
160 for (int i = 0; i < array.length; ++i) {
161 message.append("'").append(array[i]).append("'");
162
163 if (i < (array.length - 1)) {
164 message.append(", ");
165 }
166 }
167
168 message.append(" }");
169
170 fail(message.toString());
171 }
172
173 /***
174 * Asserts that the array of ints contains a given int
175 *
176 * @param expected expected int
177 * @param array the array
178 */
179 protected void assertContains(int expected, int[] array) {
180 for (int i = 0; i < array.length; ++i) {
181 if (array[i] == expected) {
182 return;
183 }
184 }
185
186 StringBuffer message = new StringBuffer();
187
188 message.append(expected).append(" not in {");
189
190 for (int i = 0; i < array.length; ++i) {
191 message.append("'").append(array[i]).append("'");
192
193 if (i < (array.length - 1)) {
194 message.append(", ");
195 }
196 }
197
198 message.append(" }");
199
200 fail(message.toString());
201 }
202
203 /***
204 * Asserts that the value of toString() on the given object matches the
205 * given text string
206 *
207 * @param value the object to be output to the console
208 * @param expected the expected String representation
209 */
210 protected void assertToString(Object value, String expected) {
211 Object console = InvokerHelper.invokeMethod(value, "toString", null);
212 assertEquals("toString() on value: " + value, expected, console);
213 }
214
215 /***
216 * Asserts that the value of inspect() on the given object matches the
217 * given text string
218 *
219 * @param value the object to be output to the console
220 * @param expected the expected String representation
221 */
222 protected void assertInspect(Object value, String expected) {
223 Object console = InvokerHelper.invokeMethod(value, "inspect", null);
224 assertEquals("inspect() on value: " + value, expected, console);
225 }
226
227 /***
228 * Asserts that the script runs without any exceptions
229 *
230 * @param script the script that should pass without any exception thrown
231 */
232 protected void assertScript(final String script) throws Exception {
233 GroovyShell shell = new GroovyShell();
234 shell.evaluate(script, getTestClassName());
235 }
236
237 protected String getTestClassName() {
238 return "TestScript" + getMethodName() + (counter++) + ".groovy";
239 }
240
241 /***
242 * Asserts that the given code closure fails when it is evaluated
243 *
244 * @param code
245 * @return the message of the thrown Throwable
246 */
247 protected String shouldFail(Closure code) {
248 boolean failed = false;
249 String result = null;
250 try {
251 code.call();
252 }
253 catch (Throwable e) {
254 failed = true;
255 result = e.getMessage();
256 }
257 assertTrue("Closure " + code + " should have failed", failed);
258 return result;
259 }
260
261 /***
262 * Asserts that the given code closure fails when it is evaluated
263 * and that a particular exception is thrown.
264 *
265 * @param clazz the class of the expected exception
266 * @param code the closure that should fail
267 * @return the message of the expected Throwable
268 */
269 protected String shouldFail(Class clazz, Closure code) {
270 Throwable th = null;
271 try {
272 code.call();
273 } catch (GroovyRuntimeException gre) {
274 th = gre;
275 while (th.getCause()!=null && th.getCause()!=gre){
276 th=th.getCause();
277 if (th!=gre && (th instanceof GroovyRuntimeException)) {
278 gre = (GroovyRuntimeException) th;
279 }
280 }
281 } catch (Throwable e) {
282 th = e;
283 }
284
285 if (th==null) {
286 fail("Closure " + code + " should have failed with an exception of type " + clazz.getName());
287 } else if (! clazz.isInstance(th)) {
288 fail("Closure " + code + " should have failed with an exception of type " + clazz.getName() + ", instead got Exception " + th);
289 }
290 return th.getMessage();
291 }
292
293 /***
294 * Returns a copy of a string in which all EOLs are \n.
295 */
296 protected String fixEOLs( String value )
297 {
298 return value.replaceAll( "(//r//n?)|\n", "\n" );
299 }
300
301 /***
302 * Runs the calling JUnit test again and fails only if it unexpectedly runs.<br/>
303 * This is helpful for tests that don't currently work but should work one day,
304 * when the tested functionality has been implemented.<br/>
305 * The right way to use it is:
306 * <pre>
307 * public void testXXX() {
308 * if (GroovyTestCase.notYetImplemented(this)) return;
309 * ... the real (now failing) unit test
310 * }
311 * </pre>
312 * Idea copied from HtmlUnit (many thanks to Marc Guillemot).
313 * Future versions maybe available in the JUnit distro.
314 * The purpose of providing a 'static' version is such that you can use the
315 * feature even if not subclassing GroovyTestCase.
316 * @return <false> when not itself already in the call stack
317 */
318 public static boolean notYetImplemented(TestCase caller) {
319 if (notYetImplementedFlag.get() != null) {
320 return false;
321 }
322 notYetImplementedFlag.set(Boolean.TRUE);
323
324 final Method testMethod = findRunningJUnitTestMethod(caller.getClass());
325 try {
326 log.info("Running " + testMethod.getName() + " as not yet implemented");
327 testMethod.invoke(caller, new Class[] {});
328 fail(testMethod.getName() + " is marked as not yet implemented but passes unexpectedly");
329 }
330 catch (final Exception e) {
331 log.info(testMethod.getName() + " fails what is normal as it is not yet implemented");
332
333 }
334 finally {
335 notYetImplementedFlag.set(null);
336 }
337 return true;
338 }
339
340 /***
341 * Convenience method for subclasses of GroovyTestCase, identical to
342 * <pre> GroovyTestCase.notYetImplemented(this); </pre>.
343 * @see #notYetImplemented(junit.framework.TestCase)
344 * @return <false> when not itself already in the call stack
345 */
346 public boolean notYetImplemented() {
347 return notYetImplemented(this);
348 }
349
350 /***
351 * From JUnit. Finds from the call stack the active running JUnit test case
352 * @return the test case method
353 * @throws RuntimeException if no method could be found.
354 */
355 private static Method findRunningJUnitTestMethod(Class caller) {
356 final Class[] args = new Class[] {};
357
358
359 final Throwable t = new Exception();
360 for (int i=t.getStackTrace().length-1; i>=0; --i) {
361 final StackTraceElement element = t.getStackTrace()[i];
362 if (element.getClassName().equals(caller.getName())) {
363 try {
364 final Method m = caller.getMethod(element.getMethodName(), args);
365 if (isPublicTestMethod(m)) {
366 return m;
367 }
368 }
369 catch (final Exception e) {
370
371 }
372 }
373 }
374 throw new RuntimeException("No JUnit test case method found in call stack");
375 }
376
377
378 /***
379 * From Junit. Test if the method is a junit test.
380 * @param method the method
381 * @return <code>true</code> if this is a junit test.
382 */
383 private static boolean isPublicTestMethod(final Method method) {
384 final String name = method.getName();
385 final Class[] parameters = method.getParameterTypes();
386 final Class returnType = method.getReturnType();
387
388 return parameters.length == 0 && name.startsWith("test")
389 && returnType.equals(Void.TYPE)
390 && Modifier.isPublic(method.getModifiers());
391 }
392
393 private static final ThreadLocal notYetImplementedFlag = new ThreadLocal();
394 }