testng源码阅读之6

两个问题

**1、tesng的架构是如何实现灵活定制listener的?通过什么方式动态加载listener
2、listener是如何抽象定义出来的**

目前常用的有reportlistenr、retryanlyzer。 testng的listener是典型的面向过程的抽象。
IConfigurationListener、IConfigurationListener2 IExecutionListener
IInvokedMethodListener、IInvokedMethodListener2 IReporter
IRetryAnalyzer ISuiteListener


以上所有的监听器,都要继承一个接口ITestNGListener

/**
 * This is a marker interface for all objects that can be passed
 * as a -listener argument.
 *
 * @author cbeust
 */
public interface ITestNGListener {
}

这个接口的主要作用。就是可以所有的接口,都可以通过instanceof来查询,得到他的类型,方便执行引擎进行辨别跟加载

接着上边说的。所有的listener都是面向过程定义的。执行引擎最终执行的部分 1、整个测试集的参数加载,配置加载 2、测试集执行前、执行后
3、测试方法执行前、执行后 4、失败是否可以重试 5、测试报告的生成

有关细节。listener如何加载到线程的上下文,如何在适当的时候使用这些listener。

核心就在TestNG这个类里边。最初的入口。在这里,会提前加载好所有的listener。
所有的listener都是定义成一个Set或者List。方便后边执行的时候使用。


public void addListener(Object listener) {
    if (! (listener instanceof ITestNGListener))
    {
      exitWithError("Listener " + listener
          + " must be one of ITestListener, ISuiteListener, IReporter, "
          + " IAnnotationTransformer, IMethodInterceptor or IInvokedMethodListener");
    }
    else {
      if (listener instanceof ISuiteListener) {
        addListener((ISuiteListener) listener);
      }
      if (listener instanceof ITestListener) {
        addListener((ITestListener) listener);
      }
      if (listener instanceof IReporter) {
        addListener((IReporter) listener);
      }
      if (listener instanceof IAnnotationTransformer) {
        setAnnotationTransformer((IAnnotationTransformer) listener);
      }
      if (listener instanceof IMethodInterceptor) {
        setMethodInterceptor((IMethodInterceptor) listener);
      }
      if (listener instanceof IInvokedMethodListener) {
        addInvokedMethodListener((IInvokedMethodListener) listener);
      }
      if (listener instanceof IHookable) {
        setHookable((IHookable) listener);
      }
      if (listener instanceof IConfigurable) {
        setConfigurable((IConfigurable) listener);
      }
      if (listener instanceof IExecutionListener) {
        addExecutionListener((IExecutionListener) listener);
      }
      if (listener instanceof IConfigurationListener) {
        getConfiguration().addConfigurationListener((IConfigurationListener) listener);
      }
    }
  }

一层层的往上configure调这里,然后是privatemain然后main入口。

下边看看具体在某个环节,他们是怎么执行的。

几乎所有的listener都是只定义了两个操作 onStart()、onFinish()

每一个层级的执行,都是用的xxxrunner的方式。比如suiteRunner、methodRunner.这些类保存了所有上下文,
而对应的suiteRunnerWorker、TestMethodWorker 就是真正的执行器。包括执行排序,执行结果,执行过程 的日志等等
他们都实现了一个共同的接口。任务超时跟优先级。同时这个是泛型的接口相对来说,抽象程度已经很高了。
如果只是实现单线程,其实没有必要继承runnable跟comparable。但是因为要支持多线程,所以要实现这个。


public class TestMethodWorker implements IWorker<ITestNGMethod>
/**
 * A runnable object that is used by {@code GraphThreadPoolExecutor} to execute
 * tasks
 */
public interface IWorker<T> extends Runnable, Comparable<IWorker<T>> {

  /**
   * @return list of tasks this worker is working on.
   */
  List<T> getTasks();

  /**
   * @return the maximum time allowed for the worker to complete the task.
   */
  long getTimeOut();

  /**
   * @return the priority of this task.
   */
  int getPriority();
}

我们看看其中一个

SuiteRunnerWorker,是如何执行的。

关键的函数,runnalbe的override。在这里执行所有的listner的onStart(),结束后运行onFinish()
由于在testNG入口类就已经校验过所有的listener的类型,跟个数,比如,只允许有一个自定义的reportlistener。所以这些listner的执行的顺序
理论上是随机的。

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

private void invokeListeners(boolean start) {
    for (ISuiteListener sl : m_listeners) {
      if (start) {
        sl.onStart(this);
      }
      else {
        sl.onFinish(this);
      }
    }
  }

好,到这里,整个框架的流程我们基本弄清楚了。一些关键的点已经厘清。
回归最开始的地方。这些listener是怎么被加载进来的呢?configure 的时候会加载。有两种方式,一种是命令行configure。
那么另外一种呢?另外一种就是直接解析xml的。这部分一般是由插件完成。实际调用的仍然是这里


public static TestNG privateMain(String[] argv, ITestListener listener) {
    TestNG result = new TestNG();

    if (null != listener) {
      result.addListener(listener);
    }

    //
    // Parse the arguments
    //
    try {
      CommandLineArgs cla = new CommandLineArgs();
      m_jCommander = new JCommander(cla, argv);
      validateCommandLineParameters(cla);
      result.configure(cla);
    }
    catch(ParameterException ex) {
      exitWithError(ex.getMessage());
    }

    //
    // Run
    //
    try {
      result.run();
    }
    catch(TestNGException ex) {
      if (TestRunner.getVerbose() > 1) {
        ex.printStackTrace(System.out);
      }
      else {
        error(ex.getMessage());
      }
      result.setStatus(HAS_FAILURE);
    }

    return result;
  }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值