View Javadoc

1   package com.zmicer.utils.junit;
2   
3   import com.zmicer.utils.InputArgumentUtils;
4   import com.zmicer.utils.LoggingUtils;
5   import com.zmicer.utils.ReflexionUtils;
6   import junit.framework.TestCase;
7   import org.apache.log4j.Logger;
8   
9   import java.io.PrintWriter;
10  import java.io.StringWriter;
11  import java.lang.reflect.Method;
12  import java.util.regex.Matcher;
13  
14  /**
15   * This class contains the functionality related to the JUnit itself and <strong>should</strong> :) be used for the increasing your
16   * productivity with junit.
17   * <br/>
18   * It contains also the tests for these method. This class is test case itself so it is not a problem to store the tests here to.
19   * note [zmicer]: if it is becoming large one - move the tests to the separated test class
20   * <p/>
21   * $Author::                    $<br/>
22   * $Rev::                       $<br/>
23   * $Date::                      $<br/>
24   */
25  public class JUnitUtils extends TestCase
26  {
27      /**
28       * Logger instance.
29       */
30      final public static Logger LOG = Logger.getLogger(JUnitUtils.class);
31  
32      /**
33       * Get the number of the tests methods at the provided class instance.
34       * todo [zmicer]: consider the necessarity of introducing the JUnitUtils class
35       *
36       * @param claz the class we need analyze. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
37       *
38       * @return the number of the test methods. In the case of this class has not method - zero (<code>0</code>) would be returned
39       */
40      public static int getTestMethodsNumber(final Class claz)
41      {
42          InputArgumentUtils.checkObjects(claz);
43          final Method[] methods = claz.getMethods();
44          int result = 0;
45          for (final Method method : methods)
46          {
47              if (method.getName().startsWith("test"))
48              {
49                  result++;
50              }
51          }
52          return result;
53      }
54  
55      /**
56       * Test the given method on the wrong args. It tests all the cases and compositions. It could be used for the unit testing as allows us
57       * not to think on the IllegalArgumentException checks - this method check all this by itself, including all the possible combinations
58       * and cases.
59       * <br/>
60       * Be noticed that in the case of smth. wrong - e.g. invalid method or class was specified - the unit tests method <code>fail</code>
61       * is invoked with the description of what went wrong
62       * <br/>
63       * <strong>Example of working of this method:<strong>
64       * there is method with two first required arguments and two last not required. Then we would check the following combinations:<br/>
65       * <code>method(null, new smth, null, null)</code>
66       * <code>method(new smth, null, null, null)</code>
67       * <br/>
68       * Be noticed this method accesses only the public method of the class you are testing using JUnit.
69       *
70       * @param object              the object on which we would check.
71       *                            Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
72       * @param methodName          the method name we would test
73       *                            Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
74       * @param requiredInfo        defines which of the parameters are required (for which the IllegalArgumentException should be thrown)
75       *                            Can not be null (otherwise <code>IllegalArgumentException</code> would appear). The number of the
76       *                            members of this array should be equal to the number of the <code>methodParamsClasses</code> arg var
77       * @param argsClasses         the Array of classes to be used, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
78       * @param args                input arguments to be used on default (without null).
79       */
80      public static void checkOnWrongArgs(final Object object, final String methodName, final boolean[] requiredInfo, final Class[] argsClasses,
81          final Object... args)
82      {
83          InputArgumentUtils.checkObjects(object, methodName, requiredInfo, argsClasses, args);
84          if (requiredInfo.length != args.length)
85          {
86              throw new IllegalArgumentException("The numbers of boolean params in the req. info is not equal to the number of the method " +
87                  "param classes");
88          }
89          try
90          {
91              final Class[] classes = new Class[args.length];
92              for (int i = 0; i < args.length; i++)
93              {
94                  if (null == args[i])
95                  {
96                      throw new IllegalArgumentException("The default argument should not contains the null objects");
97                  }
98                  classes[i] = args[i].getClass();
99              }
100             final Method method = object.getClass().getDeclaredMethod(methodName, argsClasses);
101             assertNotNull(method);
102             for (int counter = 0; counter < args.length; counter++)
103             {
104                 // only for the required argument.
105                 if (requiredInfo[counter])
106                 {
107                     final Object[] objects = new Object[args.length];
108                     int innerCounter = -1;
109                     for (final Object innerParam : args)
110                     {
111                         innerCounter++;
112                         if (innerCounter == counter || !requiredInfo[innerCounter])
113                         {
114                             objects[innerCounter] = null;
115                         }
116                         else
117                         {
118                             objects[innerCounter] = innerParam;
119                         }
120                     }
121                     // failed sub-case: exception occuried: null input arguments
122                     try
123                     {
124                         method.invoke(object, objects);
125                         fail("Exception should have been occuried before this line.");
126                     }
127                     catch (Exception ex)
128                     {
129                         assertNotNull(ex.getCause());
130                         if (!(ex.getCause() instanceof IllegalArgumentException))
131                         {
132                             LoggingUtils.logException(LOG, ex, "Incorrect Exception Occuried", object.getClass());
133                         }
134                         assertTrue(ex.getCause() instanceof IllegalArgumentException);
135                     }
136                 }
137             }
138         }
139         catch (Exception e)
140         {
141             LoggingUtils.logException(LOG, e, "Smth. went wrong", JUnitUtils.class);
142             fail("Smth. went wrong, Exception message [" + e.getMessage() + "]");
143         }
144     }
145 
146     /**
147      * Run the test which should ended with the fail result.
148      *
149      * @param object        object on which to run. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
150      * @param methodName    method to be runned. Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
151      * @param exceptionClaz exception to be thrown and then caught, Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
152      * @param argsClasses   classes of the arguments (to find the appropriate method..)
153      *                      Can not be null (otherwise <code>IllegalArgumentException</code> would appear).
154      * @param arguments     the var args defining the arguments to be passed to the running method
155      */
156     public static void runFailure(final Object object, final String methodName, final Class exceptionClaz, final Class[] argsClasses,
157         final Object... arguments)
158     {
159         InputArgumentUtils.checkObjects(object, methodName, exceptionClaz, argsClasses, arguments);
160         try
161         {
162             final Method method = object.getClass().getMethod(methodName, argsClasses);
163             assertNotNull(method);
164             try
165             {
166                 method.invoke(object, arguments);
167                 fail("Exception should have been occuried before this line.");
168             }
169             catch (Exception ex)
170             {
171                 assertNotNull(ex.getCause());
172                 if (!ReflexionUtils.instanceOf(ex.getCause(), exceptionClaz))
173                 {
174                     LoggingUtils.logException(LOG, ex, "Incorrect Exception Occuried", object.getClass());
175                 }
176                 assertTrue(ReflexionUtils.instanceOf(ex.getCause(), exceptionClaz));
177             }
178         }
179         catch (Exception e)
180         {
181             LoggingUtils.logException(LOG, e, "Smth. went wrong", JUnitUtils.class);
182             fail("Smth. went wrong, Exception message [" + e.getMessage() + "]");
183         }
184     }
185 
186     /**
187      * todo [zmicer]: the current implementation of this functionality is not correct one. Still I consider it is possible to implement
188      * this. Review how it is done for the junit - it works. If you need this functionality somewhere - just adjust this sources
189      * note [zmicer]: only for the test purposes (for JPatterns test engine allowing to setup the props/xml environment per the folders)
190      * <p/>
191      * note [zmicer]: Does not use regular expressions. On default this method without parameter uses the second method from the trace of
192      * the methods, still if you you this not directly but via another utility method building pathes e.g. for the unit test configuration
193      * please then use method with parameters.
194      *
195      * @return the name of the running method.
196      */
197     public static String getRunningTestMethodName()
198     {
199         StringWriter sw = new StringWriter();
200         PrintWriter pw = new PrintWriter(sw);
201         new Exception().printStackTrace(pw);
202         // only for the debug
203         //System.out.println(sw.toString());
204 
205         Matcher matcher = ReflexionUtils.PATTERN.matcher(sw.toString());
206         if (matcher.find() && matcher.groupCount() == 1)
207         {
208             final String res = matcher.group(1);
209             LoggingUtils.debug(ReflexionUtils.LOG, ReflexionUtils.class, "Currently running method name [" + res + "].");
210             return res;
211         }
212         LoggingUtils.debug(ReflexionUtils.LOG, ReflexionUtils.class, "Can not find the currently running method name. Smth. wrong in algorithm.");
213         return null;
214     }
215 
216     /**
217      * @return the name of the method which is runned by the given unit test. This method should be used only for unit testing (only JUnit or
218      *         maven unit tests plugin) and should be called inly at the test method itself (in simple words on the calls stack it should second after
219      *         invoke0 method of the tests runner /zero/ and test method itself /first/)
220      */
221     public static String getRunningMethodName()
222     {
223         final String testMethodName = getRunningTestMethodName();
224         if (null != testMethodName && testMethodName.startsWith("test"))
225         {
226             String temp = testMethodName.substring("test".length());
227 
228             return Character.toLowerCase(temp.charAt(0)) + temp.substring(1);
229         }
230 
231         return null;
232     }
233 }