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源代码分析:依赖管理的实现的更多相关文章

  1. hostapd源代码分析(三):管理帧的收发和处理

    hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...

  2. redis 源代码分析(一) 内存管理

    一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...

  3. Fragment事务管理源代码分析

    转载请标明出处:http://blog.csdn.net/shensky711/article/details/53132952 本文出自: [HansChen的博客] 概述 在Fragment使用中 ...

  4. cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略

    从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.R ...

  5. 读书摘要观后感与总结:《Glibc内存管理:ptmalloc2源代码分析》

    更新中 在Linux平台下做漏洞利用的时候,针对于Heap部分总是有些不求甚解,下面开个博文来记录下<Glibc内存管理:ptmalloc2源代码分析>这本书的读后感和收获,一些简单的点将 ...

  6. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  7. Gradle系列教程之依赖管理(转)

    转自Lippi-浮生志 :http://ezlippi.com/blog/2015/05/gradle-dependency-management.html 这一章我将介绍Gradle对依赖管理的强大 ...

  8. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  9. Gradle实战教程之依赖管理

    这是从我个人网站中复制过来的,原文地址:http://coolshell.info/blog/2015/05/gradle-dependency-management.html,转载请注明出处. 简要 ...

随机推荐

  1. poj3259 Wormholes (判负环)【spfa】(模板)

    <题目链接> 题目大意: John的农场里N块地,M条路连接两块地,W个虫洞,虫洞是一条单向路,会在你离开之前把你传送到目的地,就是当你过去的时候时间会倒退Ts.我们的任务是知道会不会在从 ...

  2. 洛谷 P1135 奇怪的电梯 【基础BFS】

    题目链接:https://www.luogu.org/problemnew/show/P1135 题目描述 呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯.大楼的每一层楼都可以停电梯,而且第 i 层 ...

  3. FSMN结构快速解读

    参考文献如下: (1) Feedforward Sequential Memory Neural Networks without Recurrent Feedback (2) Feedforward ...

  4. Chameleon

    # -*- coding: utf-8 -*- """ Created on Tue Dec 18 09:55:16 2018 @author: Mark,LI &quo ...

  5. EntityFramework Core

    1,安装EF Core 在.csproj中添加一下配置,用于使用dotnet ef 命令 <ItemGroup> <DotNetCliToolReference Include=&q ...

  6. C# 基于 adb 控制安卓

    我司呢,有深信服网关. 我们做项目,日常是比较“清闲”的,所以呢,深信服让你没有办法愉快的使用电脑, 但是手机是可以连外网和外面的 wifi 的,所以我们就玩手机,但是玩手机这么明显会被 Leader ...

  7. 使用TVTK库创建一个矩形视图

    from tvtk.api import tvtk # s=tvtk.ConeSource(height=,radius=) # print(s.center) #创建一个长方体数据源,并同时设置长宽 ...

  8. CocosCreator的节点显示和隐藏

    隐藏和显示有两种方式: 1.禁止节点node的运行,方法是x.node.active=false[此时隐藏了节点,且节点不再运行];恢复节点正常运行,x.node.active=true;或者使用x. ...

  9. NSArray与NSString、NSData,NSDictionary与NSString、NSData 相互转化

    NSArray *array = @[ @1, @2, @3, @4, @5, @3 ]; // 1. NSArray与NSData相互转化 // NSArray to NSData NSError ...

  10. Mac下打开多个eclipse

    命令行执行: open -n /Eclipse所在路径/Eclipse.app