TestNG源代码分析:依赖管理的实现
TestNG源代码分析:依赖管理的实现
2018-03-19
1 背景
当case之间有依赖关系,有依赖关系的case,它们的执行顺序是有限制的。TestNG提供了依赖管理功能
2 基础理论
这个执行顺序可以用拓扑排序算法实现。
只要是有向无环图就能被拓扑排序,拓扑排序维基典型实现算法:
L ← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
remove a node n from S
insert n into L
foreach node m with an edge e from n to m do
remove edge e from thegraph
if m has no other incoming edges then
insert m into S
if graph has edges then
return error (graph has at least one cycle)
else
return L (a topologically sorted order)
TestNG依赖管理功能
- dependsOnMethods
@Test
public void serverStartedOk(){}
@Test(dependsOnMethods={ "serverStartedOk" })
public void method1() {}
- dependsOnGroups
@Test(groups = { "init" })
public void serverStartedOk(){}
@Test(groups = { "init" })
public void initEnvironment(){}
@Test(dependsOnGroups = { "init.*})
public void method1() {}
3 TestNG依赖管理源代码
如何找到依赖原理源代码?
通过报错,即即故意制造一个循环依赖,然后看stack trace:
错误代码如下:
@Test(dependsOnMethods={"MethodA"})
public void MethodA(){}
Stack Trace
at org.testng.internal.Graph.topologicalSort(Graph.java:143)
at org.testng.internal.MethodHelper.topologicalSort(MethodHelper.java:231)
org.testng.TestNGException:
The following methods have cyclic dependencies:
Sample.MethodA()[pri:0, instance:Demo.Sample@50c87b21] at org.testng.internal.Graph.topologicalSort(Graph.java:143)
at org.testng.internal.MethodHelper.topologicalSort(MethodHelper.java:231)
at org.testng.internal.MethodHelper.sortMethods(MethodHelper.java:287)
at org.testng.internal.MethodHelper.collectAndOrderMethods(MethodHelper.java:60)
at org.testng.TestRunner.initMethods(TestRunner.java:464)
at org.testng.TestRunner.init(TestRunner.java:247)
at org.testng.TestRunner.init(TestRunner.java:217)
at org.testng.TestRunner.<init>(TestRunner.java:169)
at org.testng.remote.support.RemoteTestNG6_9_10$1.newTestRunner(RemoteTestNG6_9_10.java:28)
at org.testng.remote.support.RemoteTestNG6_9_10$DelegatingTestRunnerFactory.newTestRunner(RemoteTestNG6_9_10.java:61)
at org.testng.SuiteRunner$ProxyTestRunnerFactory.newTestRunner(SuiteRunner.java:594)
at org.testng.SuiteRunner.init(SuiteRunner.java:168)
at org.testng.SuiteRunner.<init>(SuiteRunner.java:117)
at org.testng.TestNG.createSuiteRunner(TestNG.java:1339)
at org.testng.TestNG.createSuiteRunners(TestNG.java:1326)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1180)
at org.testng.TestNG.runSuites(TestNG.java:1104)
at org.testng.TestNG.run(TestNG.java:1076)
at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:126)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:152)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:57)
4 源代码分析
//MethodHelper.topologicalSort
private static Graph<ITestNGMethod> topologicalSort(ITestNGMethod[] methods, List<ITestNGMethod> sequentialList, List<ITestNGMethod> parallelList)
//Graph.topologicalSort
public void topologicalSort()
注意:
- methods变量包括所有的要跑的用例和用例依赖的用例(依赖用例依赖的用例也包括)
- 依赖管理代码在Graph类中实现,MethodHelper.topologicalSort方法会调用Graph类中的方法
Graph类有三个变量
//m_nodes:放的是所有的节点的引用
private Map<T, Node<T>> m_nodes = Maps.newLinkedHashMap();
//m_independentNodes:所有独立节点的引用,这样的节点(用例)可并发运行
private List<T> m_strictlySortedNodes = null;
//m_strictlySortedNodes:经过严格排序的非独立节点
private Map<T, Node<T>> m_independentNodes = null;
Graph.topologicalSort方法实现依赖节点的拓扑排序
//m_nodes:放的是所有的节点(用例)的引用
//m_independentNodes:所有独立节点的引用
//m_strictlySortedNodes:经过严格排序的非独立节点
public void topologicalSort() {
ppp("================ SORTING");
//m_strictlySortedNodes集合: 最后的结果放到这个集合中
m_strictlySortedNodes=Lists.newArrayList();
initializeIndependentNodes(); //nodes2集合: 非独立的节点
List<Node<T>>nodes2 =Lists.newArrayList(); //1 创建非独立节点集合,即存在依赖关系的方法的集合
for (Node<T> n :getNodes()) { //getNodes()返回m_nodes
if (!isIndependent(n.getObject())){// 判断该节点是否独立,如果不独立的话,添加到nodes2中
ppp("ADDING FOR SORT: " +n.getObject());
nodes2.add(n.clone()); //使用的是clone方法来进行对象的复制,一般不推荐使用clone方法,参见Effective Java Item 11
}
else {
ppp("SKIPPING INDEPENDENT NODE" + n);
}
} //2 将非独立的节点集合排序,为了让属于同类中的方法在集合中的位置近一些,从而在调用的顺序上能够相邻一些
Collections.sort(nodes2); //3 进行拓扑排序,如果发现有循环依赖,马上抛出异常
while (!nodes2.isEmpty()) {
Node<T> node =findNodeWithNoPredecessors(nodes2); // 从nodes2集合中找到没有前驱节点的节点
if (null == node) { // 如果没有找到节点,那么创建一个Tarjan对象来得到一个cycle
List<T> cycle =newTarjan<T>(this,nodes2.get(0).getObject()).getCycle(); // 这里实现了Tarjan算法,用来得到环的路径信息
StringBuffer sb = new StringBuffer(); //在非并发环境中应该尽量使用StringBuilder
sb.append("The following methodshave cyclic dependencies:\n");
for (T m : cycle) {
sb.append(m).append("\n");
}
throw newTestNGException(sb.toString());
}
else { //如果找到了,将这个没有任何前驱节点的节点放到结果结合中,然后从nodes2集合中删除该节点
m_strictlySortedNodes.add(node.getObject());
removeFromNodes(nodes2, node);
}
}
ppp("===============DONESORTING");
if (m_verbose) {
dumpSortedNodes();
}
}
调用方法
private void initializeIndependentNodes() {
if (null == m_independentNodes) {
List<Node<T>> list = Lists.newArrayList(m_nodes.values());
Collections.sort(list);
m_independentNodes = Maps.newLinkedHashMap();
for (Node<T> node : list) {
m_independentNodes.put(node.getObject(), node);
}
}
}
private Collection<Node<T>> getNodes() {
return m_nodes.values();
}
MethodHelper.topologicalSort方法,调用Graph方法区分独立节点和依赖节点,并调用toplogicalSort进行依赖节点拓扑排序
private static Graph<ITestNGMethod> topologicalSort(ITestNGMethod[] methods, List<ITestNGMethod> sequentialList, List<ITestNGMethod> parallelList) {
Graph<ITestNGMethod> result = new Graph<>();
if (methods.length == 0) {
return result; //如果传入的methods数组长度为0,直接返回了空的graph
}
// 区分出要顺序执行的用例(依赖用例)和可并行的用例(独立用例)
for (ITestNGMethod m : methods) {
result.addNode(m); //对于每个方法instance,添加到graph中
List<ITestNGMethod> predecessors = Lists.newArrayList(); //获得该方法依赖的方法
String[] methodsDependedUpon = m.getMethodsDependedUpon();
String[] groupsDependedUpon = m.getGroupsDependedUpon();
if (methodsDependedUpon.length > 0) {
ITestNGMethod[] methodsNamed =
MethodHelper.findDependedUponMethods(m, methods);
for (ITestNGMethod pred : methodsNamed) {
predecessors.add(pred);
}
}
if (groupsDependedUpon.length > 0) {
for (String group : groupsDependedUpon) {
ITestNGMethod[] methodsThatBelongToGroup =
MethodGroupsHelper.findMethodsThatBelongToGroup(m, methods, group);
for (ITestNGMethod pred : methodsThatBelongToGroup) {
predecessors.add(pred);
}
}
}
for (ITestNGMethod predecessor : predecessor s) {
result.addPredecessor(m, predecessor); //将依赖方法加入graph中
}
}
result.topologicalSort();
sequentialList.addAll(result.getStrictlySortedNodes());
parallelList.addAll(result.getIndependentNodes());
return result;
}
调用方法
public void addNode(T tm) {
ppp("ADDING NODE " + tm + " " + tm.hashCode());
m_nodes.put(tm, new Node<>(tm));
// Initially, all the nodes are put in the independent list as well
}
public void addPredecessor(T tm, T predecessor) {
Node<T> node = findNode(tm);
if (null == node) {
throw new TestNGException("Non-existing node: " + tm);
}
else {
node.addPredecessor(predecessor);
addNeighbor(tm, predecessor);
// Remove these two nodes from the independent list
initializeIndependentNodes();
m_independentNodes.remove(predecessor);
m_independentNodes.remove(tm);
ppp(" REMOVED " + predecessor + " FROM INDEPENDENT OBJECTS");
}
}
public Set<T> getIndependentNodes() {
return m_independentNodes.keySet();
}
public List<T> getStrictlySortedNodes() {
return m_strictlySortedNodes;
}
参考
[1] TestNG源代码分析 --- 依赖管理的实现(一)
[2] TestNG源代码分析 --- 依赖管理的实现(二)
TestNG源代码分析:依赖管理的实现的更多相关文章
- hostapd源代码分析(三):管理帧的收发和处理
hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...
- redis 源代码分析(一) 内存管理
一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...
- Fragment事务管理源代码分析
转载请标明出处:http://blog.csdn.net/shensky711/article/details/53132952 本文出自: [HansChen的博客] 概述 在Fragment使用中 ...
- cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略
从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.R ...
- 读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》
更新中 在Linux平台下做漏洞利用的时候,针对于Heap部分总是有些不求甚解,下面开个博文来记录下<Glibc内存管理:ptmalloc2源代码分析>这本书的读后感和收获,一些简单的点将 ...
- android-plugmgr源代码分析
android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...
- Gradle系列教程之依赖管理(转)
转自Lippi-浮生志 :http://ezlippi.com/blog/2015/05/gradle-dependency-management.html 这一章我将介绍Gradle对依赖管理的强大 ...
- Hadoop源代码分析
http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...
- Gradle实战教程之依赖管理
这是从我个人网站中复制过来的,原文地址:http://coolshell.info/blog/2015/05/gradle-dependency-management.html,转载请注明出处. 简要 ...
随机推荐
- 《Gradle权威指南》--Android Gradle多渠道构建
No1: Build Variant = Build Type + Product Flavor Build Variant:构建的产物 Build Type:构建的类型 Product Flavor ...
- 用js来实现那些数据结构03(数组篇03-排序及多维数组)
终于,这是有关于数组的最后一篇,下一篇会真真切切给大家带来数据结构在js中的实现方式.那么这篇文章还是得啰嗦一下数组的相关知识,因为数组真的太重要了!不要怀疑数组在JS中的重要性与实用性.这篇文章分为 ...
- CKEditor 4.4.1 添加代码高亮显示插件功能--使用官方推荐Code Snippet插件
随着CKEditor4.4.1的发布,以前一直困扰的代码高亮问题终于完美的得到解决,在CKEditor4.4中官方发布了Code Snippet这个代码片段的插件,终于可以完美的内嵌使用代码高亮了,以 ...
- 喵哈哈村的魔法考试 Round #15 (Div.2) 题解
哗啦啦村的奇迹果实(一) 题解:显然答案就是最大值减去最小值. #include<bits/stdc++.h> using namespace std; const int maxn = ...
- register form
<code class="language-html"><div class="width100 marT15 content_news_list&qu ...
- java三大特性--多态(1)
定义 对象具有多种形态 类型 引用的多态: 父类的引用指向自身对象 父类的引用指向子类对象 TrafficTool traffictool=new TrafficTool();//父类的引用指向本身类 ...
- 利用java8对设计模式的重构
java8中提供的很多新特性可以用来重构传统设计模式中的写法,下面是一些示例: 一.策略模式 上图是策略模式的类图,假设我们现在要保存订单,OrderService接口定义要做什么,而NoSqlSav ...
- solr之定时增量索引实现
solr本身就提供了一个工具库实现定时增量索引,但是我在使用的过程中发现会出现一些问题,目前遇到两点: 1.启动时总是报如下异常: ? 1 The web application [solr] reg ...
- CDH:cdh5环境搭建
安装环境三台centos7 vmw: cdh- 192.168.0.141 [主节点] cdh- 192.168.0.142 [从节点] cdh- 192.168.0.143 [从节点] 1)[各节点 ...
- 微软BI 之SSIS 系列 - 通过 ROW_NUMBER 或 Script Component 为数据流输出添加行号的方法
开篇介绍 上午在天善回答看到这个问题 - SSIS 导出数据文件,能否在第一列增加一个行号,很快就帮助解决了,方法就是在 SQL 查询的时候加一个 ROW_NUMBER() 就可以了. 后来想起在两年 ...