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;
}
原文地址:https://www.cnblogs.com/season-xie/p/7811363.html