surefire接着调入testng中的方法,testNG.run -> runSuites -> runSutiesLocally -> runSuitesSequentially
-> SuiteRunnerWorker.run -> runSuites
-> SuiteRunner.run -> privateRun -> invokeTestMethods
->testng.internal.TestMethodWorker.run -> invokeTestMethods
->testng.internal.Invoker.invokeTestMethods -> invokeMethod
->testng.internal.MethodInvocationHelper.invokeMethod
前面源码贴到了SuiteRunner,接下来TestMethodWorker类:
@Override
public void run() {
for (IMethodInstance testMthdInst : m_methodInstances) {
ITestNGMethod testMethod = testMthdInst.getMethod();
ITestClass testClass = testMethod.getTestClass();
invokeBeforeClassMethods(testClass, testMthdInst);
// Invoke test method
try {
invokeTestMethods(testMethod, testMthdInst.getInstance(), m_testContext);
}
finally {
invokeAfterClassMethods(testClass, testMthdInst);
}
}
}
protected void invokeTestMethods(ITestNGMethod tm, Object instance,
ITestContext testContext)
{
// Potential bug here: we look up the method index of tm among all
// the test methods (not very efficient) but if this method appears
// several times and these methods are run in parallel, the results
// are unpredictable... Need to think about this more (and make it
// more efficient)
List<ITestResult> testResults =
m_invoker.invokeTestMethods(tm,
m_suite,
m_parameters,
m_groupMethods,
instance,
testContext);
if (testResults != null) {
m_testResults.addAll(testResults);
}
}还有其他两个关键方法:
protected void invokeBeforeClassMethods(ITestClass testClass, IMethodInstance mi) {
// if no BeforeClass than return immediately
// used for parallel case when BeforeClass were already invoked
if( (null == m_classMethodMap) || (null == m_classMethodMap.getInvokedBeforeClassMethods())) {
return;
}
ITestNGMethod[] classMethods= testClass.getBeforeClassMethods();
if(null == classMethods || classMethods.length == 0) {
return;
}
// the whole invocation must be synchronized as other threads must
// get a full initialized test object (not the same for @After)
Map<ITestClass, Set<Object>> invokedBeforeClassMethods =
m_classMethodMap.getInvokedBeforeClassMethods();
// System.out.println("SYNCHRONIZING ON " + testClass
// + " thread:" + Thread.currentThread().getId()
// + " invokedMap:" + invokedBeforeClassMethods.hashCode() + " "
// + invokedBeforeClassMethods);
synchronized(testClass) {
Set<Object> instances= invokedBeforeClassMethods.get(testClass);
if(null == instances) {
instances= new HashSet<Object>();
invokedBeforeClassMethods.put(testClass, instances);
}
for(Object instance: mi.getInstances()) {
if (! instances.contains(instance)) {
instances.add(instance);
m_invoker.invokeConfigurations(testClass,
testClass.getBeforeClassMethods(),
m_suite,
m_parameters,
null, /* no parameter values */
instance);
}
}
}
}
/**
* Invoke the @AfterClass methods if not done already
* @param testClass
* @param mi
*/
protected void invokeAfterClassMethods(ITestClass testClass, IMethodInstance mi) {
// if no BeforeClass than return immediately
// used for parallel case when BeforeClass were already invoked
if( (null == m_classMethodMap) || (null == m_classMethodMap.getInvokedAfterClassMethods()) ) {
return;
}
ITestNGMethod[] afterClassMethods= testClass.getAfterClassMethods();
if(null == afterClassMethods || afterClassMethods.length == 0) {
return;
}
//
// Invoke after class methods if this test method is the last one
//
List<Object> invokeInstances= Lists.newArrayList();
ITestNGMethod tm= mi.getMethod();
if (m_classMethodMap.removeAndCheckIfLast(tm, mi.getInstance())) {
Map<ITestClass, Set<Object>> invokedAfterClassMethods
= m_classMethodMap.getInvokedAfterClassMethods();
synchronized(invokedAfterClassMethods) {
Set<Object> instances = invokedAfterClassMethods.get(testClass);
if(null == instances) {
instances= new HashSet<Object>();
invokedAfterClassMethods.put(testClass, instances);
}
for(Object inst: mi.getInstances()) {
if(! instances.contains(inst)) {
invokeInstances.add(inst);
}
}
}
for(Object inst: invokeInstances) {
m_invoker.invokeConfigurations(testClass,
afterClassMethods,
m_suite,
m_parameters,
null, /* no parameter values */
inst);
}
}
}Invoker类代码:
@Override
public List<ITestResult> invokeTestMethods(ITestNGMethod testMethod,
XmlSuite suite,
Map<String, String> testParameters,
ConfigurationGroupMethods groupMethods,
Object instance,
ITestContext testContext)
{
// Potential bug here if the test method was declared on a parent class
assert null != testMethod.getTestClass()
: "COULDN'T FIND TESTCLASS FOR " + testMethod.getRealClass();
if (!MethodHelper.isEnabled(testMethod.getMethod(), m_annotationFinder)) {
// return if the method is not enabled. No need to do any more calculations
return Collections.emptyList();
}
// By the time this testMethod to be invoked,
// all dependencies should be already run or we need to skip this method,
// so invocation count should not affect dependencies check
final String okToProceed = checkDependencies(testMethod, testContext.getAllTestMethods());
if (okToProceed != null) {
//
// Not okToProceed. Test is being skipped
//
ITestResult result = registerSkippedTestResult(testMethod, null, System.currentTimeMillis(),
new Throwable(okToProceed));
m_notifier.addSkippedTest(testMethod, result);
return Collections.singletonList(result);
}
final Map<String, String> parameters =
testMethod.findMethodParameters(testContext.getCurrentXmlTest());
// For invocationCount > 1 and threadPoolSize > 1 run this method in its own pool thread.
if (testMethod.getInvocationCount() > 1 && testMethod.getThreadPoolSize() > 1) {
return invokePooledTestMethods(testMethod, suite, parameters, groupMethods, testContext);
}
long timeOutInvocationCount = testMethod.getInvocationTimeOut();
//FIXME: Is this correct?
boolean onlyOne = testMethod.getThreadPoolSize() > 1 ||
timeOutInvocationCount > 0;
int invocationCount = onlyOne ? 1 : testMethod.getInvocationCount();
ExpectedExceptionsHolder expectedExceptionHolder =
MethodHelper.findExpectedExceptions(m_annotationFinder, testMethod.getMethod());
final ITestClass testClass= testMethod.getTestClass();
final List<ITestResult> result = Lists.newArrayList();
final FailureContext failure = new FailureContext();
final ITestNGMethod[] beforeMethods = filterMethods(testClass, testClass.getBeforeTestMethods(), CAN_RUN_FROM_CLASS);
final ITestNGMethod[] afterMethods = filterMethods(testClass, testClass.getAfterTestMethods(), CAN_RUN_FROM_CLASS);
while(invocationCount-- > 0) {
if(false) {
// Prevent code formatting
}
//
// No threads, regular invocation
//
else {
// Used in catch statement
long start = System.currentTimeMillis();
Map<String, String> allParameterNames = Maps.newHashMap();
ParameterBag bag = createParameters(testMethod,
parameters, allParameterNames, suite, testContext, instance);
if (bag.hasErrors()) {
final ITestResult tr = bag.errorResult;
tr.setStatus(ITestResult.SKIP);
runTestListeners(tr);
m_notifier.addSkippedTest(testMethod, tr);
result.add(tr);
continue;
}
Iterator<Object[]> allParameterValues = bag.parameterHolder.parameters;
int parametersIndex = 0;
try {
List<TestMethodWithDataProviderMethodWorker> workers = Lists.newArrayList();
if (bag.parameterHolder.origin == ParameterOrigin.ORIGIN_DATA_PROVIDER &&
bag.parameterHolder.dataProviderHolder.annotation.isParallel()) {
while (allParameterValues.hasNext()) {
Object[] parameterValues = injectParameters(allParameterValues.next(),
testMethod.getMethod(), testContext, null /* test result */);
TestMethodWithDataProviderMethodWorker w =
new TestMethodWithDataProviderMethodWorker(this,
testMethod, parametersIndex,
parameterValues, instance, suite, parameters, testClass,
beforeMethods, afterMethods, groupMethods,
expectedExceptionHolder, testContext, m_skipFailedInvocationCounts,
invocationCount, failure.count, m_notifier);
workers.add(w);
// testng387: increment the param index in the bag.
parametersIndex++;
}
PoolService<List<ITestResult>> ps =
new PoolService<List<ITestResult>>(suite.getDataProviderThreadCount());
List<List<ITestResult>> r = ps.submitTasksAndWait(workers);
for (List<ITestResult> l2 : r) {
result.addAll(l2);
}
} else {
while (allParameterValues.hasNext()) {
Object[] parameterValues = injectParameters(allParameterValues.next(),
testMethod.getMethod(), testContext, null /* test result */);
List<ITestResult> tmpResults = Lists.newArrayList();
try {
tmpResults.add(invokeTestMethod(instance,
testMethod,
parameterValues,
parametersIndex,
suite,
parameters,
testClass,
beforeMethods,
afterMethods,
groupMethods, failure));
}
finally {
if (failure.instances.isEmpty()) {
result.addAll(tmpResults);
} else {
for (Object failedInstance : failure.instances) {
List<ITestResult> retryResults = Lists.newArrayList();
failure.count = retryFailed(
failedInstance, testMethod, suite, testClass, beforeMethods,
afterMethods, groupMethods, retryResults,
failure.count, expectedExceptionHolder,
testContext, parameters, parametersIndex);
result.addAll(retryResults);
}
}
//
// If we have a failure, skip all the
// other invocationCounts
//
if (failure.count > 0
&& (m_skipFailedInvocationCounts
|| testMethod.skipFailedInvocations())) {
while (invocationCount-- > 0) {
result.add(registerSkippedTestResult(testMethod, instance, System.currentTimeMillis(), null));
}
break;
}
}// end finally
parametersIndex++;
}
}
}
catch (Throwable cause) {
ITestResult r =
new TestResult(testMethod.getTestClass(),
instance,
testMethod,
cause,
start,
System.currentTimeMillis(),
m_testContext);
r.setStatus(TestResult.FAILURE);
result.add(r);
runTestListeners(r);
m_notifier.addFailedTest(testMethod, r);
} // catch
}
}
return result;
} // invokeTestMethod
private ITestResult registerSkippedTestResult(ITestNGMethod testMethod, Object instance,
long start, Throwable throwable) {
ITestResult result =
new TestResult(testMethod.getTestClass(),
instance,
testMethod,
throwable,
start,
System.currentTimeMillis(),
m_testContext);
result.setStatus(TestResult.SKIP);
runTestListeners(result);
return result;
}
/**
* Gets an array of parameter values returned by data provider or the ones that
* are injected based on parameter type. The method also checks for {@code NoInjection}
* annotation
* @param parameterValues parameter values from a data provider
* @param method method to be invoked
* @param context test context
* @param testResult test result
* @return
*/
private Object[] injectParameters(Object[] parameterValues, Method method,
ITestContext context, ITestResult testResult)
throws TestNGException {
List<Object> vResult = Lists.newArrayList();
int i = 0;
int numValues = parameterValues.length;
int numParams = method.getParameterTypes().length;
if (numValues > numParams && ! method.isVarArgs()) {
throw new TestNGException("The data provider is trying to pass " + numValues
+ " parameters but the method "
+ method.getDeclaringClass().getName() + "#" + method.getName()
+ " takes " + numParams);
}
// beyond this, numValues <= numParams
for (Class<?> cls : method.getParameterTypes()) {
Annotation[] annotations = method.getParameterAnnotations()[i];
boolean noInjection = false;
for (Annotation a : annotations) {
if (a instanceof NoInjection) {
noInjection = true;
break;
}
}
Object injected = Parameters.getInjectedParameter(cls, method, context, testResult);
if (injected != null && ! noInjection) {
vResult.add(injected);
} else {
try {
if (method.isVarArgs()) vResult.add(parameterValues);
else vResult.add(parameterValues[i++]);
} catch (ArrayIndexOutOfBoundsException ex) {
throw new TestNGException("The data provider is trying to pass " + numValues
+ " parameters but the method "
+ method.getDeclaringClass().getName() + "#" + method.getName()
+ " takes " + numParams
+ " and TestNG is unable in inject a suitable object", ex);
}
}
}
return vResult.toArray(new Object[vResult.size()]);
}
private ParameterBag handleParameters(ITestNGMethod testMethod,
Object instance,
Map<String, String> allParameterNames,
Map<String, String> parameters,
Object[] parameterValues,
XmlSuite suite,
ITestContext testContext,
Object fedInstance,
ITestResult testResult)
{
try {
return new ParameterBag(
Parameters.handleParameters(testMethod,
allParameterNames,
instance,
new Parameters.MethodParameters(parameters,
testMethod.findMethodParameters(testContext.getCurrentXmlTest()),
parameterValues,
testMethod.getMethod(), testContext, testResult),
suite,
m_annotationFinder,
fedInstance));
}
// catch(TestNGException ex) {
// throw ex;
// }
catch(Throwable cause) {
return new ParameterBag(
new TestResult(
testMethod.getTestClass(),
instance,
testMethod,
cause,
System.currentTimeMillis(),
System.currentTimeMillis(),
m_testContext));
}
}
/**
* Invokes a method that has a specified threadPoolSize.
*/
private List<ITestResult> invokePooledTestMethods(ITestNGMethod testMethod,
XmlSuite suite,
Map<String, String> parameters,
ConfigurationGroupMethods groupMethods,
ITestContext testContext)
{
//
// Create the workers
//
List<IWorker<ITestNGMethod>> workers = Lists.newArrayList();
// Create one worker per invocationCount
for (int i = 0; i < testMethod.getInvocationCount(); i++) {
// we use clones for reporting purposes
ITestNGMethod clonedMethod= testMethod.clone();
clonedMethod.setInvocationCount(1);
clonedMethod.setThreadPoolSize(1);
MethodInstance mi = new MethodInstance(clonedMethod);
workers.add(new SingleTestMethodWorker(this,
mi,
suite,
parameters,
testContext));
}
return runWorkers(testMethod, workers, testMethod.getThreadPoolSize(), groupMethods, suite, parameters);
}
static class FailureContext {
int count = 0;
List<Object> instances = Lists.newArrayList();
}
/**
* @param testMethod
* @param result
* @param expectedExceptionsHolder
* @param failure
* @return
*/
void handleInvocationResults(ITestNGMethod testMethod,
List<ITestResult> result,
ExpectedExceptionsHolder expectedExceptionsHolder,
boolean triggerListeners,
boolean collectResults,
FailureContext failure)
{
//
// Go through all the results and create a TestResult for each of them
//
List<ITestResult> resultsToRetry = Lists.newArrayList();
for (ITestResult testResult : result) {
Throwable ite= testResult.getThrowable();
int status= testResult.getStatus();
boolean handled = false;
// Exception thrown?
if (ite != null) {
// Invocation caused an exception, see if the method was annotated with @ExpectedException
if (isExpectedException(ite, expectedExceptionsHolder)) {
if (messageRegExpMatches(expectedExceptionsHolder.messageRegExp, ite)) {
testResult.setStatus(ITestResult.SUCCESS);
status= ITestResult.SUCCESS;
}
else {
testResult.setThrowable(
new TestException("The exception was thrown with the wrong message:" +
" expected \"" + expectedExceptionsHolder.messageRegExp + "\"" +
" but got \"" + ite.getMessage() + "\"", ite));
status= ITestResult.FAILURE;
}
} else if (isSkipExceptionAndSkip(ite)){
status = ITestResult.SKIP;
} else if (expectedExceptionsHolder != null) {
testResult.setThrowable(
new TestException("Expected exception of " +
getExpectedExceptionsPluralize(expectedExceptionsHolder)
+ " but got " + ite, ite));
status= ITestResult.FAILURE;
} else {
handleException(ite, testMethod, testResult, failure.count++);
handled = true;
status = testResult.getStatus();
}
}
// No exception thrown, make sure we weren't expecting one
else if(status != ITestResult.SKIP && expectedExceptionsHolder != null) {
Class<?>[] classes = expectedExceptionsHolder.expectedClasses;
if (classes != null && classes.length > 0) {
testResult.setThrowable(
new TestException("Method " + testMethod + " should have thrown an exception of "
+ getExpectedExceptionsPluralize(expectedExceptionsHolder)));
status= ITestResult.FAILURE;
}
}
testResult.setStatus(status);
if (status == ITestResult.FAILURE && !handled) {
handleException(ite, testMethod, testResult, failure.count++);
status = testResult.getStatus();
}
if (status == ITestResult.FAILURE) {
IRetryAnalyzer retryAnalyzer = testMethod.getRetryAnalyzer();
if (retryAnalyzer != null && failure.instances != null && retryAnalyzer.retry(testResult)) {
resultsToRetry.add(testResult);
failure.instances.add(testResult.getInstance());
}
}
if (collectResults) {
// Collect the results
collectResults(testMethod, Collections.singleton(testResult));
// if (triggerListeners && status != ITestResult.SUCCESS) {
// runTestListeners(testResult);
// }
}
} // for results
removeResultsToRetryFromResult(resultsToRetry, result, failure);
}
private String getExpectedExceptionsPluralize(final ExpectedExceptionsHolder holder) {
StringBuilder sb = new StringBuilder();
if (holder.expectedClasses.length > 1) {
sb.append("any of types ");
sb.append(Arrays.toString(holder.expectedClasses));
} else {
sb.append("type ");
sb.append(holder.expectedClasses[0]);
}
return sb.toString();
}Invoker类代码较多,还有:
private void invokeConfigurations(IClass testClass,
ITestNGMethod currentTestMethod,
ITestNGMethod[] allMethods,
XmlSuite suite,
Map<String, String> params,
Object[] parameterValues,
Object instance,
ITestResult testMethodResult)
{
if(null == allMethods) {
log(5, "No configuration methods found");
return;
}
ITestNGMethod[] methods= filterMethods(testClass, allMethods, SAME_CLASS);
for(ITestNGMethod tm : methods) {
if(null == testClass) {
testClass= tm.getTestClass();
}
ITestResult testResult= new TestResult(testClass,
instance,
tm,
null,
System.currentTimeMillis(),
System.currentTimeMillis(),
m_testContext);
IConfigurationAnnotation configurationAnnotation= null;
try {
Object inst = tm.getInstance();
if (inst == null) {
inst = instance;
}
Class<?> objectClass= inst.getClass();
Method method= tm.getMethod();
// Only run the configuration if
// - the test is enabled and
// - the Configuration method belongs to the same class or a parent
if(MethodHelper.isEnabled(objectClass, m_annotationFinder)) {
configurationAnnotation = AnnotationHelper.findConfiguration(m_annotationFinder, method);
if (MethodHelper.isEnabled(configurationAnnotation)) {
boolean alwaysRun= isAlwaysRun(configurationAnnotation);
if (!confInvocationPassed(tm, currentTestMethod, testClass, instance) && !alwaysRun) {
handleConfigurationSkip(tm, testResult, configurationAnnotation, currentTestMethod, instance, suite);
continue;
}
log(3, "Invoking " + Utils.detailedMethodName(tm, true));
Object[] parameters = Parameters.createConfigurationParameters(tm.getMethod(),
params,
parameterValues,
currentTestMethod,
m_annotationFinder,
suite,
m_testContext,
testMethodResult);
testResult.setParameters(parameters);
Object newInstance = null != instance ? instance: inst;
runConfigurationListeners(testResult, true /* before */);
invokeConfigurationMethod(newInstance, tm,
parameters, testResult);
// TODO: probably we should trigger the event for each instance???
testResult.setEndMillis(System.currentTimeMillis());
runConfigurationListeners(testResult, false /* after */);
}
else {
log(3,
"Skipping "
+ Utils.detailedMethodName(tm, true)
+ " because it is not enabled");
}
} // if is enabled
else {
log(3,
"Skipping "
+ Utils.detailedMethodName(tm, true)
+ " because "
+ objectClass.getName()
+ " is not enabled");
}
}
catch(InvocationTargetException ex) {
handleConfigurationFailure(ex, tm, testResult, configurationAnnotation, currentTestMethod, instance, suite);
}
catch(TestNGException ex) {
// Don't wrap TestNGExceptions, it could be a missing parameter on a
// @Configuration method
handleConfigurationFailure(ex, tm, testResult, configurationAnnotation, currentTestMethod, instance, suite);
}
catch(Throwable ex) { // covers the non-wrapper exceptions
handleConfigurationFailure(ex, tm, testResult, configurationAnnotation, currentTestMethod, instance, suite);
}
} // for methods
}
/**
* Marks the current <code>TestResult</code> as skipped and invokes the listeners.
*/
private void handleConfigurationSkip(ITestNGMethod tm,
ITestResult testResult,
IConfigurationAnnotation annotation,
ITestNGMethod currentTestMethod,
Object instance,
XmlSuite suite) {
recordConfigurationInvocationFailed(tm, testResult.getTestClass(), annotation, currentTestMethod, instance, suite);
testResult.setStatus(ITestResult.SKIP);
runConfigurationListeners(testResult, false /* after */);
}
/**
* Is the <code>IConfiguration</code> marked as alwaysRun.
*/
private boolean isAlwaysRun(IConfigurationAnnotation configurationAnnotation) {
if(null == configurationAnnotation) {
return false;
}
boolean alwaysRun= false;
if ((configurationAnnotation.getAfterSuite()
|| configurationAnnotation.getAfterTest()
|| configurationAnnotation.getAfterTestClass()
|| configurationAnnotation.getAfterTestMethod())
&& configurationAnnotation.getAlwaysRun())
{
alwaysRun= true;
}
return alwaysRun;
}
private void handleConfigurationFailure(Throwable ite,
ITestNGMethod tm,
ITestResult testResult,
IConfigurationAnnotation annotation,
ITestNGMethod currentTestMethod,
Object instance,
XmlSuite suite)
{
Throwable cause= ite.getCause() != null ? ite.getCause() : ite;
if(isSkipExceptionAndSkip(cause)) {
testResult.setThrowable(cause);
handleConfigurationSkip(tm, testResult, annotation, currentTestMethod, instance, suite);
return;
}
Utils.log("", 3, "Failed to invoke configuration method "
+ tm.getRealClass().getName() + "." + tm.getMethodName() + ":" + cause.getMessage());
handleException(cause, tm, testResult, 1);
runConfigurationListeners(testResult, false /* after */);
//
// If in TestNG mode, need to take a look at the annotation to figure out
// what kind of @Configuration method we're dealing with
//
if (null != annotation) {
recordConfigurationInvocationFailed(tm, testResult.getTestClass(), annotation, currentTestMethod, instance, suite);
}
}
/**
* @return All the classes that belong to the same <test> tag as @param cls
*/
private XmlClass[] findClassesInSameTest(Class<?> cls, XmlSuite suite) {
Map<String, XmlClass> vResult= Maps.newHashMap();
String className= cls.getName();
for(XmlTest test : suite.getTests()) {
for(XmlClass testClass : test.getXmlClasses()) {
if(testClass.getName().equals(className)) {
// Found it, add all the classes in this test in the result
for(XmlClass thisClass : test.getXmlClasses()) {
vResult.put(thisClass.getName(), thisClass);
}
// Note: we need to iterate through the entire suite since the same
// class might appear in several <test> tags
}
}
}
XmlClass[] result= vResult.values().toArray(new XmlClass[vResult.size()]);
return result;
}
/**
* Record internally the failure of a Configuration, so that we can determine
* later if @Test should be skipped.
*/
private void recordConfigurationInvocationFailed(ITestNGMethod tm,
IClass testClass,
IConfigurationAnnotation annotation,
ITestNGMethod currentTestMethod,
Object instance,
XmlSuite suite) {
// If beforeTestClass or afterTestClass failed, mark either the config method's
// entire class as failed, or the class under tests as failed, depending on
// the configuration failure policy
if (annotation.getBeforeTestClass() || annotation.getAfterTestClass()) {
// tm is the configuration method, and currentTestMethod is null for BeforeClass
// methods, so we need testClass
if (m_continueOnFailedConfiguration) {
setClassInvocationFailure(testClass.getRealClass(), instance);
} else {
setClassInvocationFailure(tm.getRealClass(), instance);
}
}
// If before/afterTestMethod failed, mark either the config method's entire
// class as failed, or just the current test method as failed, depending on
// the configuration failure policy
else if (annotation.getBeforeTestMethod() || annotation.getAfterTestMethod()) {
if (m_continueOnFailedConfiguration) {
setMethodInvocationFailure(currentTestMethod, instance);
} else {
setClassInvocationFailure(tm.getRealClass(), instance);
}
}
// If beforeSuite or afterSuite failed, mark *all* the classes as failed
// for configurations. At this point, the entire Suite is screwed
else if (annotation.getBeforeSuite() || annotation.getAfterSuite()) {
m_suiteState.failed();
}
// beforeTest or afterTest: mark all the classes in the same
// <test> stanza as failed for configuration
else if (annotation.getBeforeTest() || annotation.getAfterTest()) {
setClassInvocationFailure(tm.getRealClass(), instance);
XmlClass[] classes= findClassesInSameTest(tm.getRealClass(), suite);
for(XmlClass xmlClass : classes) {
setClassInvocationFailure(xmlClass.getSupportClass(), instance);
}
}
String[] beforeGroups= annotation.getBeforeGroups();
if(null != beforeGroups && beforeGroups.length > 0) {
for(String group: beforeGroups) {
m_beforegroupsFailures.put(group, Boolean.FALSE);
}
}
}private void invokeConfigurationMethod(Object targetInstance,
ITestNGMethod tm,
Object[] params,
ITestResult testResult)
throws InvocationTargetException, IllegalAccessException
{
// Mark this method with the current thread id
tm.setId(ThreadUtil.currentThreadInfo());
{
InvokedMethod invokedMethod= new InvokedMethod(targetInstance,
tm,
params,
System.currentTimeMillis(),
testResult);
runInvokedMethodListeners(BEFORE_INVOCATION, invokedMethod, testResult);
m_notifier.addInvokedMethod(invokedMethod);
try {
Reporter.setCurrentTestResult(testResult);
Method method = tm.getMethod();
//
// If this method is a IConfigurable, invoke its run() method
//
IConfigurable configurableInstance =
IConfigurable.class.isAssignableFrom(tm.getMethod().getDeclaringClass()) ?
(IConfigurable) targetInstance : m_configuration.getConfigurable();
if (configurableInstance != null) {
MethodInvocationHelper.invokeConfigurable(targetInstance, params, configurableInstance, method,
testResult);
}
else {
//
// Not a IConfigurable, invoke directly
//
if (MethodHelper.calculateTimeOut(tm) <= 0) {
MethodInvocationHelper.invokeMethod(method, targetInstance, params);
}
else {
MethodInvocationHelper.invokeWithTimeout(tm, targetInstance, params, testResult);
if (!testResult.isSuccess()) {
// A time out happened
throwConfigurationFailure(testResult, testResult.getThrowable());
throw testResult.getThrowable();
}
}
}
}
catch (InvocationTargetException ex) {
throwConfigurationFailure(testResult, ex);
throw ex;
}
catch (IllegalAccessException ex) {
throwConfigurationFailure(testResult, ex);
throw ex;
}
catch (NoSuchMethodException ex) {
throwConfigurationFailure(testResult, ex);
throw new TestNGException(ex);
}
catch (Throwable ex) {
throwConfigurationFailure(testResult, ex);
throw new TestNGException(ex);
}
finally {
Reporter.setCurrentTestResult(testResult);
runInvokedMethodListeners(AFTER_INVOCATION, invokedMethod, testResult);
Reporter.setCurrentTestResult(null);
}
}
}
private void throwConfigurationFailure(ITestResult testResult, Throwable ex)
{
testResult.setStatus(ITestResult.FAILURE);;
testResult.setThrowable(ex.getCause() == null ? ex : ex.getCause());
}
private void runInvokedMethodListeners(InvokedMethodListenerMethod listenerMethod, IInvokedMethod invokedMethod,
ITestResult testResult)
{
if ( noListenersPresent() ) {
return;
}
InvokedMethodListenerInvoker invoker = new InvokedMethodListenerInvoker(listenerMethod, testResult, m_testContext);
for (IInvokedMethodListener currentListener : m_invokedMethodListeners) {
invoker.invokeListener(currentListener, invokedMethod);
}
}
private boolean noListenersPresent() {
return (m_invokedMethodListeners == null) || (m_invokedMethodListeners.size() == 0);
}
// pass both paramValues and paramIndex to be thread safe in case parallel=true + dataprovider.
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();
//
// Invoke beforeGroups configurations
//
invokeBeforeGroupsConfigurations(testClass, tm, groupMethods, suite, params,
instance);
//
// Invoke beforeMethods only if
// - firstTimeOnly is not set
// - firstTimeOnly is set, and we are reaching at the first invocationCount
//
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());
// If no timeOut, just invoke the method
if (MethodHelper.calculateTimeOut(tm) <= 0) {
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 (hookableInstance != null) {
MethodInvocationHelper.invokeHookable(instance,
parameterValues, hookableInstance, thisMethod, testResult);
}
//
// Not a IHookable, invoke directly
//
else {
MethodInvocationHelper.invokeMethod(thisMethod, instance,
parameterValues);
}
testResult.setStatus(ITestResult.SUCCESS);
}
else {
//
// Method with a timeout
//
Reporter.setCurrentTestResult(testResult);
MethodInvocationHelper.invokeWithTimeout(tm, instance, parameterValues, testResult);
}
}
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
= MethodHelper.findExpectedExceptions(m_annotationFinder, tm.getMethod());
List<ITestResult> results = Lists.<ITestResult>newArrayList(testResult);
handleInvocationResults(tm, results, expectedExceptionClasses, false,
false /* collect results */, 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);
// Do not notify if will retry.
if (!results.isEmpty()) {
collectResults(tm, Collections.<ITestResult>singleton(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);
}
return testResult;
}
void collectResults(ITestNGMethod testMethod, Collection<ITestResult> results) {
for (ITestResult result : results) {
// Collect the results
final int status = result.getStatus();
if(ITestResult.SUCCESS == status) {
m_notifier.addPassedTest(testMethod, result);
}
else if(ITestResult.SKIP == status) {
m_notifier.addSkippedTest(testMethod, result);
}
else if(ITestResult.FAILURE == status) {
m_notifier.addFailedTest(testMethod, result);
}
else if(ITestResult.SUCCESS_PERCENTAGE_FAILURE == status) {
m_notifier.addFailedButWithinSuccessPercentageTest(testMethod, result);
}
else {
assert false : "UNKNOWN STATUS:" + status;
}
}
}MethodInvocationHelper类代码:protected static Object invokeMethod(Method thisMethod, Object instance, Object[] parameters)
throws InvocationTargetException, IllegalAccessException {
Utils.checkInstanceOrStatic(instance, thisMethod);
// TESTNG-326, allow IObjectFactory to load from non-standard classloader
// If the instance has a different classloader, its class won't match the
// method's class
if (instance == null || !thisMethod.getDeclaringClass().isAssignableFrom(instance.getClass())) {
// for some reason, we can't call this method on this class
// is it static?
boolean isStatic = Modifier.isStatic(thisMethod.getModifiers());
if (!isStatic) {
// not static, so grab a method with the same name and signature in this case
Class<?> clazz = instance.getClass();
try {
thisMethod = clazz.getMethod(thisMethod.getName(), thisMethod.getParameterTypes());
} catch (Exception e) {
// ignore, the method may be private
boolean found = false;
for (; clazz != null; clazz = clazz.getSuperclass()) {
try {
thisMethod = clazz.getDeclaredMethod(thisMethod.getName(),
thisMethod.getParameterTypes());
found = true;
break;
} catch (Exception e2) {
}
}
if (!found) {
// should we assert here? Or just allow it to fail on invocation?
if (thisMethod.getDeclaringClass().getName().equals(instance.getClass().getName())) {
throw new RuntimeException("Can't invoke method " + thisMethod
+ ", probably due to classloader mismatch");
}
throw new RuntimeException("Can't invoke method " + thisMethod
+ " on this instance of " + instance.getClass() + " due to class mismatch");
}
}
}
}
synchronized(thisMethod) {
if (! Modifier.isPublic(thisMethod.getModifiers())) {
thisMethod.setAccessible(true);
}
}
return thisMethod.invoke(instance, parameters);
}
protected static Iterator<Object[]> invokeDataProvider(Object instance, Method dataProvider,
ITestNGMethod method, ITestContext testContext, Object fedInstance,
IAnnotationFinder annotationFinder) {
Iterator<Object[]> result;
final ConstructorOrMethod com = method.getConstructorOrMethod();
// If it returns an Object[][], convert it to an Iterable<Object[]>
try {
List<Object> lParameters = Lists.newArrayList();
// Go through all the parameters declared on this Data Provider and
// make sure we have at most one Method and one ITestContext.
// Anything else is an error
Class<?>[] parameterTypes = dataProvider.getParameterTypes();
final Collection<Pair<Integer, Class<?>>> unresolved = new ArrayList<Pair<Integer, Class<?>>>(parameterTypes.length);
int i = 0;
for (Class<?> cls : parameterTypes) {
boolean isTestInstance = annotationFinder.hasTestInstance(dataProvider, i++);
if (cls.equals(Method.class)) {
lParameters.add(com.getMethod());
} else if (cls.equals(Constructor.class)) {
lParameters.add(com.getConstructor());
} else if (cls.equals(ConstructorOrMethod.class)) {
lParameters.add(com);
} else if (cls.equals(ITestNGMethod.class)) {
lParameters.add(method);
} else if (cls.equals(ITestContext.class)) {
lParameters.add(testContext);
} else if (isTestInstance) {
lParameters.add(fedInstance);
} else {
unresolved.add(new Pair<Integer, Class<?>>(i, cls));
}
}
if (!unresolved.isEmpty()) {
final StringBuilder sb = new StringBuilder();
sb.append("Some DataProvider ").append(dataProvider).append(" parameters unresolved: ");
for (Pair<Integer, Class<?>> pair : unresolved) {
sb.append(" at ").append(pair.first()).append(" type ").append(pair.second()).append("\n");
}
throw new TestNGException(sb.toString());
}
Object[] parameters = lParameters.toArray(new Object[lParameters.size()]);
Class<?> returnType = dataProvider.getReturnType();
if (Object[][].class.isAssignableFrom(returnType)) {
Object[][] originalResult = (Object[][]) invokeMethod(dataProvider, instance, parameters);
// If the data provider is restricting the indices to return, filter them out
int[] indices = dataProvider.getAnnotation(DataProvider.class).indices();
Object[][] oResult;
if (indices.length > 0) {
oResult = new Object[indices.length][];
for (int j = 0; j < indices.length; j++) {
oResult[j] = originalResult[indices[j]];
}
} else {
oResult = originalResult;
}
method.setParameterInvocationCount(oResult.length);
result = MethodHelper.createArrayIterator(oResult);
} else if (Iterator.class.isAssignableFrom(returnType)) {
// Already an Iterator<Object[]>, assign it directly
result = (Iterator<Object[]>) invokeMethod(dataProvider, instance, parameters);
} else {
throw new TestNGException("Data Provider " + dataProvider + " must return"
+ " either Object[][] or Iterator<Object>[], not " + returnType);
}
} catch (InvocationTargetException e) {
// Don't throw TestNGException here or this test won't be reported as a
// skip or failure
throw new RuntimeException(e.getCause());
} catch (IllegalAccessException e) {
// Don't throw TestNGException here or this test won't be reported as a
// skip or failure
throw new RuntimeException(e.getCause());
}
return result;
}
TestNG 测试框架详解
本文深入剖析了 TestNG 测试框架的内部运行机制,详细介绍了其核心组件 Surefire、SuiteRunner、TestMethodWorker 和 Invoker 的工作流程。此外,还探讨了配置方法的执行时机、并行测试执行策略及异常处理机制。
321

被折叠的 条评论
为什么被折叠?



