testng源码阅读之6

来源:互联网 发布:怎么开店淘宝店 编辑:程序博客网 时间:2024/05/22 01:31

两个问题

**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;  }
原创粉丝点击