testng TestListener 原理简析
1. 在xmlSuite 解析 tag="listeners"的信息,加入addListener
public class XmlSuite @OnElementList(tag = "listeners", attributes = { "class-name" }) public void onListenerElement(String className) { addListener(className); }
2.在运行test的时候,设置好不同运行状态(pass,failed)再运行 runTestListeners(testResult),同时把testresult传入
private ITestResult invokeMethod(Object instance, final ITestNGMethod tm, Object[] parameterValues, int parametersIndex, XmlSuite suite, Map<String, String> params, ITestClass testClass, ITestNGMethod[] beforeMethods, ITestNGMethod[] afterMethods, ConfigurationGroupMethods groupMethods, FailureContext failureContext) { TestResult testResult = new TestResult(); invokeBeforeGroupsConfigurations(testClass, tm, groupMethods, suite, params, instance); invokeConfigurations(testClass, tm, filterConfigurationMethods(tm, beforeMethods, true /* beforeMethods */), suite, params, parameterValues, instance, testResult); // // Create the ExtraOutput for this method // InvokedMethod invokedMethod = null; try { testResult.init(testClass, instance, tm, null, System.currentTimeMillis(), 0, m_testContext); testResult.setParameters(parameterValues); testResult.setHost(m_testContext.getHost()); testResult.setStatus(ITestResult.STARTED); invokedMethod= new InvokedMethod(instance, tm, parameterValues, System.currentTimeMillis(), testResult); // Fix from ansgarkonermann // invokedMethod is used in the finally, which can be invoked if // any of the test listeners throws an exception, therefore, // invokedMethod must have a value before we get here runTestListeners(testResult); runInvokedMethodListeners(BEFORE_INVOCATION, invokedMethod, testResult); m_notifier.addInvokedMethod(invokedMethod); Method thisMethod = tm.getConstructorOrMethod().getMethod(); if(confInvocationPassed(tm, tm, testClass, instance)) { log(3, "Invoking " + tm.getRealClass().getName() + "." + tm.getMethodName()); Reporter.setCurrentTestResult(testResult); // If this method is a IHookable, invoke its run() method IHookable hookableInstance = IHookable.class.isAssignableFrom(tm.getRealClass()) ? (IHookable) instance : m_configuration.getHookable(); if (MethodHelper.calculateTimeOut(tm) <= 0) { if (hookableInstance != null) { MethodInvocationHelper.invokeHookable(instance, parameterValues, hookableInstance, thisMethod, testResult); } else { // Not a IHookable, invoke directly MethodInvocationHelper.invokeMethod(thisMethod, instance, parameterValues); } testResult.setStatus(ITestResult.SUCCESS); } else { // Method with a timeout MethodInvocationHelper.invokeWithTimeout(tm, instance, parameterValues, testResult, hookableInstance); } } else { testResult.setStatus(ITestResult.SKIP); } } catch(InvocationTargetException ite) { testResult.setThrowable(ite.getCause()); testResult.setStatus(ITestResult.FAILURE); } catch(ThreadExecutionException tee) { // wrapper for TestNGRuntimeException Throwable cause= tee.getCause(); if(TestNGRuntimeException.class.equals(cause.getClass())) { testResult.setThrowable(cause.getCause()); } else { testResult.setThrowable(cause); } testResult.setStatus(ITestResult.FAILURE); } catch(Throwable thr) { // covers the non-wrapper exceptions testResult.setThrowable(thr); testResult.setStatus(ITestResult.FAILURE); } finally { // Set end time ASAP testResult.setEndMillis(System.currentTimeMillis()); ExpectedExceptionsHolder expectedExceptionClasses = new ExpectedExceptionsHolder(m_annotationFinder, tm, new RegexpExpectedExceptionsHolder(m_annotationFinder, tm)); List<ITestResult> results = Lists.<ITestResult>newArrayList(testResult); handleInvocationResults(tm, results, expectedExceptionClasses, failureContext); // If this method has a data provider and just failed, memorize the number // at which it failed. // Note: we're not exactly testing that this method has a data provider, just // that it has parameters, so might have to revisit this if bugs get reported // for the case where this method has parameters that don't come from a data // provider if (testResult.getThrowable() != null && parameterValues.length > 0) { tm.addFailedInvocationNumber(parametersIndex); } // // Increment the invocation count for this method // tm.incrementCurrentInvocationCount(); // Run invokedMethodListeners after updating TestResult runInvokedMethodListeners(AFTER_INVOCATION, invokedMethod, testResult); runTestListeners(testResult); // // Invoke afterMethods only if // - lastTimeOnly is not set // - lastTimeOnly is set, and we are reaching the last invocationCount // invokeConfigurations(testClass, tm, filterConfigurationMethods(tm, afterMethods, false /* beforeMethods */), suite, params, parameterValues, instance, testResult); // // Invoke afterGroups configurations // invokeAfterGroupsConfigurations(testClass, tm, groupMethods, suite, params, instance); // Reset the test result last. If we do this too early, Reporter.log() // invocations from listeners will be discarded Reporter.setCurrentTestResult(null); }
3.通过testresult的状态分别调用 testlistener的各种方法(onstart,onfailed....),支持多个ITestListener噢
public static void runTestListeners(ITestResult tr, List<ITestListener> listeners) { for (ITestListener itl : listeners) { switch(tr.getStatus()) { case ITestResult.SKIP: { itl.onTestSkipped(tr); break; } case ITestResult.SUCCESS_PERCENTAGE_FAILURE: { itl.onTestFailedButWithinSuccessPercentage(tr); break; } case ITestResult.FAILURE: { itl.onTestFailure(tr); break; } case ITestResult.SUCCESS: { itl.onTestSuccess(tr); break; } case ITestResult.STARTED: { itl.onTestStart(tr); break; } default: { assert false : "UNKNOWN STATUS:" + tr; } } } } return testResult; }