View Javadoc

    1   /*
    2    $Id: GroovyTestCase.java,v 1.27 2006/02/16 18:35:45 dierk Exp $
    3
    4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5
    6    Redistribution and use of this software and associated documentation
    7    ("Software"), with or without modification, are permitted provided
    8    that the following conditions are met:
    9
    10   1. Redistributions of source code must retain copyright
    11      statements and notices.  Redistributions must also contain a
    12      copy of this document.
    13
    14   2. Redistributions in binary form must reproduce the
    15      above copyright notice, this list of conditions and the
    16      following disclaimer in the documentation and/or other
    17      materials provided with the distribution.
    18
    19   3. The name "groovy" must not be used to endorse or promote
    20      products derived from this Software without prior written
    21      permission of The Codehaus.  For written permission,
    22      please contact info@codehaus.org.
    23
    24   4. Products derived from this Software may not be called "groovy"
    25      nor may "groovy" appear in their names without prior written
    26      permission of The Codehaus. "groovy" is a registered
    27      trademark of The Codehaus.
    28
    29   5. Due credit should be given to The Codehaus -
    30      http://groovy.codehaus.org/
    31
    32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    43   OF THE POSSIBILITY OF SUCH DAMAGE.
    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){ // if wrapped, find the root cause
    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             // method execution failed, it is really "not yet implemented"
    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         // search the inial junit test
    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                     // can't access, ignore it
    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 }