美文网首页TestNG与JUnit
TestNG框架源码走读二:测试套执行

TestNG框架源码走读二:测试套执行

作者: 测试你个头 | 来源:发表于2018-03-06 09:57 被阅读35次

    TestNG框架源码走读一:入口

    上一篇主要走读TestNG类的main函数入口,其中TestNG#runSuitesLocally()执行测试套的方法有3个核心逻辑需要详细走读下代码:
    1.createSuiteRunners实现SuiteRunner的实例化。
    2.runSuiteSequentially实现测试套的串行执行。
    3.populateSuiteGraph/GraphThreadPoolExecutor实现测试套批量并行执行。

    一、SuiteRunner实例化

    在上一篇TestNG#runSuitesLocally()的源码中:


    TestNG#createSuiteRunners()的源码如下:
    核心逻辑是递归调用TestNG#createSuiteRunner()为每个测试套创建SuiteRunner实例:

      private void createSuiteRunners(SuiteRunnerMap suiteRunnerMap /* OUT */, XmlSuite xmlSuite) {
        // 根据命令行参数设置测试套的参数
        ......
        
        // 测试套下test的method-selector设置
        ......
        
        // createSuiteRunner(xmlSuite)为测试套创建执行器
        suiteRunnerMap.put(xmlSuite, createSuiteRunner(xmlSuite));
        
        // 递归为测试套下的子测试套创建执行器
        for (XmlSuite childSuite : xmlSuite.getChildSuites()) {
          createSuiteRunners(suiteRunnerMap, childSuite);
        }
      }
    

    TestNG#createSuiteRunner()的源码如下:
    1.调用SuiteRunner的构造器方法获得实例化对象result。
    2.向result赋值测试套的一些监听器实例。
    3.最终返回result对象,完成SuiteRunner对象的创建。

      private SuiteRunner createSuiteRunner(XmlSuite xmlSuite) {
        // SuiteRunner的实例化
        SuiteRunner result = new SuiteRunner(getConfiguration(), xmlSuite,
            m_outputDir,
            m_testRunnerFactory,
            m_useDefaultListeners,
            m_methodInterceptors,
            m_invokedMethodListeners.values(),
            m_testListeners.values(),
            m_classListeners.values());
        
        // 添加测试套的开始/结束事件监听器
        for (ISuiteListener isl : m_suiteListeners.values()) {
          result.addListener(isl);
        }
    
        // 添加报告生成的监听器,所有测试套执行结束时调用(Listener的调用代码在TestNG#run()中)
        for (IReporter r : result.getReporters()) {
          maybeAddListener(m_reporters, r.getClass(), r, true);
        }
    
        // 
        for (IConfigurationListener cl : m_configuration.getConfigurationListeners()) {
          result.addConfigurationListener(cl);
        }
    
        return result;
      }
    

    上面就是SuiteRunner的实例化过程,具体SuiteRunner构造器方法中干了什么先略过,继续看看为测试套创建SuiteRunner实例后调用TestNG#runSuitesSequentially()如何实现测试套的执行。

    二、调用SuiteRunner.run()执行测试套

    1.走读TestNG#runSuitesSequentially()的源码,发现调用链如下:TestNG#runSuitesSequentially()->SuiteRunnerWorker#runSuite()->SuiteRunner#run(),横跨3个类后最终调用了SuiteRunner#run()

      // SuiteRunnerWorker#runSuite()源码
      private void runSuite(SuiteRunnerMap suiteRunnerMap, XmlSuite xmlSuite)
      {
        SuiteRunner suiteRunner = (SuiteRunner) suiteRunnerMap.get(xmlSuite);
        suiteRunner.run();
      }
    

    2.SuiteRunner.run()如何实现执行测试套?
    SuiteRunner#run()的源码如下,主要就是调用SuiteRunner#privateRun()以及前后监听器方法。

      @Override
      public void run() {
        invokeListeners(true /* start */);
        try {
          privateRun();
        }
        finally {
          invokeListeners(false /* stop */);
        }
      }
    

    对照走读的代码注释,SuiteRunner#privateRun()的源码核心逻辑如下:

    • 1.从SuiteRunner.testRunners中获取测试套前置/后置方法
    • 2.调用测试套前置方法
    • 3.调用SuiteRunner#runSequentially()和SuiteRunner#runInParallelTestMode()执行所有testRunner(用例执行器)
    • 4.调用测试套后置方法。
        private void privateRun() {
    
        // Map for unicity, Linked for guaranteed order
        Map<Method, ITestNGMethod> beforeSuiteMethods= new LinkedHashMap<>();
        Map<Method, ITestNGMethod> afterSuiteMethods = new LinkedHashMap<>();
    
        IInvoker invoker = null;
    
        // 1.从testRunner集合中获取beforeSuite/afterSuite方法
        for (TestRunner tr: testRunners) {
          invoker = tr.getInvoker();
          
          for (ITestNGMethod m : tr.getBeforeSuiteMethods()) {
            beforeSuiteMethods.put(m.getConstructorOrMethod().getMethod(), m);
          }
    
          for (ITestNGMethod m : tr.getAfterSuiteMethods()) {
            afterSuiteMethods.put(m.getConstructorOrMethod().getMethod(), m);
          }
        }
    
        // 2.调用beforeSuite方法
        if (invoker != null) {
          if(! beforeSuiteMethods.values().isEmpty()) {
            invoker.invokeConfigurations(null,
                beforeSuiteMethods.values().toArray(new ITestNGMethod[beforeSuiteMethods.size()]),
                xmlSuite, xmlSuite.getParameters(), null, /* no parameter values */
                null /* instance */
            );
          }
    
          Utils.log("SuiteRunner", 3, "Created " + testRunners.size() + " TestRunners");
    
          // 3.执行所有的test runners
          boolean testsInParallel = XmlSuite.ParallelMode.TESTS.equals(xmlSuite.getParallel());
          if (!testsInParallel) {
            runSequentially();
          }
          else {
            runInParallelTestMode();
          }
    
          // 4.调用afterSuite方法
          if (! afterSuiteMethods.values().isEmpty()) {
            invoker.invokeConfigurations(null,
                  afterSuiteMethods.values().toArray(new ITestNGMethod[afterSuiteMethods.size()]),
                xmlSuite, xmlSuite.getAllParameters(), null, /* no parameter values */
    
                  null /* instance */);
          }
        }
    

    如果进一步深入,需要了解:

    • SuiteRunner.testRunners是如何初始化的?
      beforeSuite/afterSuite方法取自testRunner的属性,那么testRunner的m_beforeSuiteMethods/m_afterSuiteMethods是如何赋值的?
    • SuiteRunner#runSequentially()和SuiteRunner#runInParallelTestMode()内部实现如何?

    这些是接下来需要了解的TestRunner如何实现测试套下用例的执行。

    相关文章

      网友评论

        本文标题:TestNG框架源码走读二:测试套执行

        本文链接:https://www.haomeiwen.com/subject/wzrqnxtx.html