Testng 测试框架源码阅读(二)
生活随笔
收集整理的這篇文章主要介紹了
Testng 测试框架源码阅读(二)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
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類:
@Overridepublic void run() {for (IMethodInstance testMthdInst : m_methodInstances) {ITestNGMethod testMethod = testMthdInst.getMethod();ITestClass testClass = testMethod.getTestClass();invokeBeforeClassMethods(testClass, testMthdInst);// Invoke test methodtry {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 invokedif( (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 invokedif( (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類代碼:
@Overridepublic 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 classassert 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 calculationsreturn 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 checkfinal 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 statementlong 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 finallyparametersIndex++;}}}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;} // invokeTestMethodprivate 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 <= numParamsfor (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 invocationCountfor (int i = 0; i < testMethod.getInvocationCount(); i++) {// we use clones for reporting purposesITestNGMethod 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 @ExpectedExceptionif (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 oneelse 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 resultscollectResults(testMethod, Collections.singleton(testResult)); // if (triggerListeners && status != ITestResult.SUCCESS) { // runTestListeners(testResult); // }}} // for resultsremoveResultsToRetryFromResult(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 parentif(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 enabledelse {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 methodhandleConfigurationFailure(ex, tm, testResult, configurationAnnotation, currentTestMethod, instance, suite);}catch(Throwable ex) { // covers the non-wrapper exceptionshandleConfigurationFailure(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 resultfor(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 policyif (annotation.getBeforeTestClass() || annotation.getAfterTestClass()) {// tm is the configuration method, and currentTestMethod is null for BeforeClass// methods, so we need testClassif (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 policyelse 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 screwedelse if (annotation.getBeforeSuite() || annotation.getAfterSuite()) {m_suiteState.failed();}// beforeTest or afterTest: mark all the classes in the same// <test> stanza as failed for configurationelse 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 idtm.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 happenedthrowConfigurationFailure(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 hererunTestListeners(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 methodif (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 TestNGRuntimeExceptionThrowable 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 exceptionstestResult.setThrowable(thr);testResult.setStatus(ITestResult.FAILURE);}finally {// Set end time ASAPtestResult.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// providerif (testResult.getThrowable() != null && parameterValues.length > 0) {tm.addFailedInvocationNumber(parametersIndex);}//// Increment the invocation count for this method//tm.incrementCurrentInvocationCount();// Run invokedMethodListeners after updating TestResultrunInvokedMethodListeners(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 discardedReporter.setCurrentTestResult(null);}return testResult;}void collectResults(ITestNGMethod testMethod, Collection<ITestResult> results) {for (ITestResult result : results) {// Collect the resultsfinal 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 classif (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 caseClass<?> clazz = instance.getClass();try {thisMethod = clazz.getMethod(thisMethod.getName(), thisMethod.getParameterTypes());} catch (Exception e) {// ignore, the method may be privateboolean 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 errorClass<?>[] 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 outint[] 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 directlyresult = (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 failurethrow new RuntimeException(e.getCause());} catch (IllegalAccessException e) {// Don't throw TestNGException here or this test won't be reported as a// skip or failurethrow new RuntimeException(e.getCause());}return result;}
總結
以上是生活随笔為你收集整理的Testng 测试框架源码阅读(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: phpcms如何做关键字
- 下一篇: surefire 拉起testng单元测