testng源码阅读之三

来源:互联网 发布:2016电脑安全软件排行 编辑:程序博客网 时间:2024/05/17 02:43

接着上文继续分析testRunner的privateRun的流程。
上一篇讲清楚了,测试方法执行前的一些准备工作。包括执行的方法,顺序等等。

接下来要分析两个事情:
1、testrunner的上下文从何而来,如何定义的
2、测试结果是如何收集的,就是testresult的多线程处理

/**   * Run all the ITestNGMethods passed in through the constructor.   *   * @see java.lang.Runnable#run()   */  @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.getInstances(), m_testContext);      }      finally {        invokeAfterClassMethods(testClass, testMthdInst);      }    }  }

多线程的run方法的定义。上下文是m_testContext。这变量在TestMethodWorker里边是final

 private final ITestContext m_testContext; public TestMethodWorker(IInvoker invoker,                          IMethodInstance[] testMethods,                          XmlSuite suite,                          Map<String, String> parameters,                          ITestNGMethod[] allTestMethods,                          ConfigurationGroupMethods groupMethods,                          ClassMethodMap classMethodMap,                          ITestContext testContext)  {

层层调用。一步步的回朔回去。

testRunner的调用。上下文传的是testrunner本身。

public class TestRunner    implements ITestContext, ITestResultNotifier, IThreadWorkerFactory<ITestNGMethod>@retinder-->这里是因为TestRunner本身就实现了ITestContext;private TestMethodWorker createTestMethodWorker(      List<IMethodInstance> methodInstances, Map<String, String> params,      Class<?> c) {    return new TestMethodWorker(m_invoker,        findClasses(methodInstances, c),        m_xmlTest.getSuite(),        params,        m_allTestMethods,        m_groupMethods,        m_classMethodMap,        this);  }

既然上下文是TestRunner本身,我们看看这个类Getter跟Setter。
这里写图片描述

这里写图片描述

上下文主要包括配置(configuration)、监听器(listener)、before、after这些。所有的东西,都在构造函数里边初始化。

public TestRunner(IConfiguration configuration, ISuite suite, XmlTest test,      boolean skipFailedInvocationCounts,      List<IInvokedMethodListener> listeners) {    init(configuration, suite, test, suite.getOutputDirectory(),        suite.getAnnotationFinder(),        skipFailedInvocationCounts, listeners);  }  private void init(IConfiguration configuration,                    ISuite suite,                    XmlTest test,                    String outputDirectory,                    IAnnotationFinder annotationFinder,                    boolean skipFailedInvocationCounts,                    List<IInvokedMethodListener> invokedMethodListeners)  {    m_configuration = configuration;    m_xmlTest= test;    m_suite = suite;    m_testName = test.getName();    m_host = suite.getHost();    m_testClassesFromXml= test.getXmlClasses();    m_skipFailedInvocationCounts = skipFailedInvocationCounts;    setVerbose(test.getVerbose());    boolean preserveOrder = "true".equalsIgnoreCase(test.getPreserveOrder());    m_methodInterceptor = preserveOrder ? new PreserveOrderMethodInterceptor()        : new InstanceOrderingMethodInterceptor();    m_packageNamesFromXml= test.getXmlPackages();    if(null != m_packageNamesFromXml) {      for(XmlPackage xp: m_packageNamesFromXml) {        m_testClassesFromXml.addAll(xp.getXmlClasses());      }    }    m_annotationFinder= annotationFinder;    m_invokedMethodListeners = invokedMethodListeners;    m_invoker = new Invoker(m_configuration, this, this, m_suite.getSuiteState(),        m_skipFailedInvocationCounts, invokedMethodListeners);    if (suite.getParallel() != null) {      log(3, "Running the tests in '" + test.getName() + "' with parallel mode:" + suite.getParallel());    }    setOutputDirectory(outputDirectory);    // Finish our initialization    init();  }

然后我们来看看他被哪些方法调用,是如何跟最初的入口链接起来的。
这里写图片描述

这里的就是SuiteRunner会调用的地方。这里的方法是属于其中的静态内部类。

private static class DefaultTestRunnerFactory implements ITestRunnerFactory {

一步步跟踪回去,最终是落在了这个方法

private void runSuitesSequentially(XmlSuite xmlSuite,      SuiteRunnerMap suiteRunnerMap, int verbose, String defaultSuiteName) {    for (XmlSuite childSuite : xmlSuite.getChildSuites()) {      runSuitesSequentially(childSuite, suiteRunnerMap, verbose, defaultSuiteName);    }    SuiteRunnerWorker srw = new SuiteRunnerWorker(suiteRunnerMap.get(xmlSuite), suiteRunnerMap,      verbose, defaultSuiteName);    srw.run();  }

最终被入口的run方法调用
这里写图片描述

里边有非常非常多的细节。但是源码阅读主要在厘清脉络,参考其中优秀的设计。具体的业务细节,可以在遇到问题在详细研究。

这里边其实就是简单的Ioc的应用。面向接口,首先为了保证一个worker对应一个runner,在给worker上线文的时候,本身runner实现了ItestContext接口,然后把自身作为一个参数传给worker。
实现事实上的链式调用。即新开一个runner实例,即会附带新开一个worker实例,这两是一一对应的。这个对多线程编程的场景特别重要。

作为一个测试框架,并发场景主要存在于,参数的准确跟执行的速度足够快,并不存在资源争用、竞争,所以TestNg的这套设计,对于测试工具本身,还是很有启发意义的。

现在来看第二个问题:
测试结果的收集,尤其是多线程下的结果收集。其实在分析完第一个问题,我们已经有结论了。由于避开了资源争用,所以其实在所有的worker执行完之后,所有的结果放在对应的runner里边。在总入口,或者reporter里边,做汇总就可以了。现在来看看我们猜测的是否正确

 /**   * Run TestNG.   */  public void run() {    initializeSuitesAndJarFile();    initializeConfiguration();    initializeDefaultListeners();    initializeCommandLineSuites();    initializeCommandLineSuitesParams();    initializeCommandLineSuitesGroups();    sanityCheck();    List<ISuite> suiteRunners = null;    runExecutionListeners(true /* start */);    m_start = System.currentTimeMillis();    //    // Slave mode    //    if (m_slavefileName != null) {       SuiteSlave slave = new SuiteSlave( m_slavefileName, this );       slave.waitForSuites();    }    //    // Regular mode    //    else if (m_masterfileName == null) {      suiteRunners = runSuitesLocally();    }    //    // Master mode    //    else {       SuiteDispatcher dispatcher = new SuiteDispatcher(m_masterfileName);       suiteRunners = dispatcher.dispatch(getConfiguration(),           m_suites, getOutputDirectory(),           getTestListeners());    }    m_end = System.currentTimeMillis();    runExecutionListeners(false /* finish */);    if(null != suiteRunners) {      @retinder-->果然,生成报告就是直接用的runner      generateReports(suiteRunners);    }    if(!m_hasTests) {      setStatus(HAS_NO_TEST);      if (TestRunner.getVerbose() > 1) {        System.err.println("[TestNG] No tests found. Nothing was run");        usage();      }    }  }

结论是,测试结果的汇总并不涉及到多线程。

原创粉丝点击