1package org.testng.junit; 2 3 4import java.lang.reflect.Constructor; 5import org.testng.ITestListener; 6import org.testng.ITestNGMethod; 7import org.testng.ITestResult; 8import org.testng.TestNGException; 9import org.testng.collections.Lists; 10import org.testng.internal.ITestResultNotifier; 11import org.testng.internal.InvokedMethod; 12 13import java.lang.reflect.InvocationTargetException; 14import java.lang.reflect.Method; 15import java.lang.reflect.Modifier; 16import java.util.Calendar; 17import java.util.Collection; 18import java.util.List; 19import java.util.Map; 20import java.util.WeakHashMap; 21 22import junit.framework.AssertionFailedError; 23import junit.framework.Test; 24import junit.framework.TestListener; 25import junit.framework.TestResult; 26import junit.framework.TestSuite; 27import org.testng.*; 28 29/** 30 * A JUnit TestRunner that records/triggers all information/events necessary to TestNG. 31 * 32 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a> 33 */ 34public class JUnitTestRunner implements TestListener, IJUnitTestRunner { 35 public static final String SUITE_METHODNAME = "suite"; 36 37 private ITestResultNotifier m_parentRunner; 38 39 private Map<Test, TestRunInfo> m_tests= new WeakHashMap<>(); 40 private List<ITestNGMethod> m_methods= Lists.newArrayList(); 41 private Collection<IInvokedMethodListener> m_invokedMethodListeners = Lists.newArrayList(); 42 43 public JUnitTestRunner() { 44 } 45 46 public JUnitTestRunner(ITestResultNotifier tr) { 47 m_parentRunner= tr; 48 } 49 50 /** 51 * Needed from TestRunner in order to figure out what JUnit test methods were run. 52 * 53 * @return the list of all JUnit test methods run 54 */ 55 @Override 56 public List<ITestNGMethod> getTestMethods() { 57 return m_methods; 58 } 59 60 @Override 61 public void setTestResultNotifier(ITestResultNotifier notifier) { 62 m_parentRunner= notifier; 63 } 64 65 /** 66 * @see junit.framework.TestListener#startTest(junit.framework.Test) 67 */ 68 @Override 69 public void startTest(Test test) { 70 m_tests.put(test, new TestRunInfo(Calendar.getInstance().getTimeInMillis())); 71 } 72 73 74 /** 75 * @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable) 76 */ 77 @Override 78 public void addError(Test test, Throwable t) { 79 recordFailure(test, t); 80 } 81 82 /** 83 * @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError) 84 */ 85 @Override 86 public void addFailure(Test test, AssertionFailedError t) { 87 recordFailure(test, t); 88 } 89 90 private void recordFailure(Test test, Throwable t) { 91 TestRunInfo tri= m_tests.get(test); 92 if(null != tri) { 93 tri.setThrowable(t); 94 } 95 } 96 97 /** 98 * @see junit.framework.TestListener#endTest(junit.framework.Test) 99 */ 100 @Override 101 public void endTest(Test test) { 102 TestRunInfo tri= m_tests.get(test); 103 if(null == tri) { 104 return; // HINT: this should never happen. How do I protect myself? 105 } 106 107 org.testng.internal.TestResult tr= recordResults(test, tri); 108 109 runTestListeners(tr, m_parentRunner.getTestListeners()); 110 } 111 112 public void setInvokedMethodListeners(Collection<IInvokedMethodListener> listeners) { 113 m_invokedMethodListeners = listeners; 114 } 115 116 117 private org.testng.internal.TestResult recordResults(Test test, TestRunInfo tri) { 118 JUnitTestClass tc= new JUnit3TestClass(test); 119 JUnitTestMethod tm= new JUnit3TestMethod(tc, test); 120 121 org.testng.internal.TestResult tr= new org.testng.internal.TestResult(tc, 122 test, 123 tm, 124 tri.m_failure, 125 tri.m_start, 126 Calendar.getInstance().getTimeInMillis(), 127 null); 128 129 if(tri.isFailure()) { 130 tr.setStatus(ITestResult.FAILURE); 131 m_parentRunner.addFailedTest(tm, tr); 132 } 133 else { 134 m_parentRunner.addPassedTest(tm, tr); 135 } 136 137 InvokedMethod im = new InvokedMethod(test, tm, new Object[0], tri.m_start, tr); 138 m_parentRunner.addInvokedMethod(im); 139 m_methods.add(tm); 140 for (IInvokedMethodListener l: m_invokedMethodListeners) { 141 l.beforeInvocation(im, tr); 142 } 143 144 return tr; 145 } 146 147 private static void runTestListeners(ITestResult tr, List<ITestListener> listeners) { 148 for (ITestListener itl : listeners) { 149 switch(tr.getStatus()) { 150 case ITestResult.SKIP: { 151 itl.onTestSkipped(tr); 152 break; 153 } 154 case ITestResult.SUCCESS_PERCENTAGE_FAILURE: { 155 itl.onTestFailedButWithinSuccessPercentage(tr); 156 break; 157 } 158 case ITestResult.FAILURE: { 159 itl.onTestFailure(tr); 160 break; 161 } 162 case ITestResult.SUCCESS: { 163 itl.onTestSuccess(tr); 164 break; 165 } 166 167 case ITestResult.STARTED: { 168 itl.onTestStart(tr); 169 break; 170 } 171 172 default: { 173 assert false : "UNKNOWN STATUS:" + tr; 174 } 175 } 176 } 177 } 178 179 /** 180 * Returns the Test corresponding to the given suite. This is 181 * a template method, subclasses override runFailed(), clearStatus(). 182 */ 183 protected Test getTest(Class testClass, String... methods) { 184 if (methods.length > 0) { 185 TestSuite ts = new TestSuite(); 186 try { 187 Constructor c = testClass.getConstructor(String.class); 188 for (String m: methods) { 189 try { 190 ts.addTest((Test) c.newInstance(m)); 191 } catch (InstantiationException ex) { 192 runFailed(testClass, "abstract class " + ex); 193 } catch (IllegalAccessException ex) { 194 runFailed(testClass, "constructor is not public " + ex); 195 } catch (IllegalArgumentException ex) { 196 runFailed(testClass, "actual and formal parameters differ " + ex); 197 } catch (InvocationTargetException ex) { 198 runFailed(testClass, "exception while instatiating test for method '" + m + "' " + ex); 199 } 200 } 201 } catch (NoSuchMethodException ex) { 202 runFailed(testClass, "no constructor accepting String argument found " + ex); 203 } catch (SecurityException ex) { 204 runFailed(testClass, "security exception " + ex); 205 } 206 return ts; 207 } 208 Method suiteMethod = null; 209 try { 210 suiteMethod = testClass.getMethod(SUITE_METHODNAME, new Class[0]); 211 } 212 catch (Exception e) { 213 214 // try to extract a test suite automatically 215 return new TestSuite(testClass); 216 } 217 if (!Modifier.isStatic(suiteMethod.getModifiers())) { 218 runFailed(testClass, "suite() method must be static"); 219 220 return null; 221 } 222 Test test = null; 223 try { 224 test = (Test) suiteMethod.invoke(null, (Object[]) new Class[0]); // static method 225 if (test == null) { 226 return test; 227 } 228 } 229 catch (InvocationTargetException e) { 230 runFailed(testClass, "failed to invoke method suite():" + e.getTargetException().toString()); 231 232 return null; 233 } 234 catch (IllegalAccessException e) { 235 runFailed(testClass, "failed to invoke method suite():" + e.toString()); 236 237 return null; 238 } 239 240 return test; 241 } 242 243 /** 244 * A <code>start</code> implementation that ignores the <code>TestResult</code> 245 * @param testClass the JUnit test class 246 */ 247 @Override 248 public void run(Class testClass, String... methods) { 249 start(testClass, methods); 250 } 251 252 /** 253 * Starts a test run. Analyzes the command line arguments and runs the given 254 * test suite. 255 */ 256 public TestResult start(Class testCase, String... methods) { 257 try { 258 Test suite = getTest(testCase, methods); 259 260 if(null != suite) { 261 return doRun(suite); 262 } 263 else { 264 runFailed(testCase, "could not create/run JUnit test suite"); 265 } 266 } 267 catch (Exception e) { 268 runFailed(testCase, "could not create/run JUnit test suite: " + e.getMessage()); 269 } 270 271 return null; 272 } 273 274 protected void runFailed(Class clazz, String message) { 275 throw new TestNGException("Failure in JUnit mode for class " + clazz.getName() + ": " + message); 276 } 277 278 /** 279 * Creates the TestResult to be used for the test run. 280 */ 281 protected TestResult createTestResult() { 282 return new TestResult(); 283 } 284 285 protected TestResult doRun(Test suite) { 286 TestResult result = createTestResult(); 287 result.addListener(this); 288 suite.run(result); 289 290 return result; 291 } 292 293 private static class TestRunInfo { 294 private final long m_start; 295 private Throwable m_failure; 296 297 public TestRunInfo(long start) { 298 m_start= start; 299 } 300 301 public boolean isFailure() { 302 return null != m_failure; 303 } 304 305 public void setThrowable(Throwable t) { 306 m_failure= t; 307 } 308 } 309} 310