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,转载请注明出处. 简要 ...
随机推荐
- poj3259 Wormholes (判负环)【spfa】(模板)
<题目链接> 题目大意: John的农场里N块地,M条路连接两块地,W个虫洞,虫洞是一条单向路,会在你离开之前把你传送到目的地,就是当你过去的时候时间会倒退Ts.我们的任务是知道会不会在从 ...
- 洛谷 P1135 奇怪的电梯 【基础BFS】
题目链接:https://www.luogu.org/problemnew/show/P1135 题目描述 呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯.大楼的每一层楼都可以停电梯,而且第 i 层 ...
- FSMN结构快速解读
参考文献如下: (1) Feedforward Sequential Memory Neural Networks without Recurrent Feedback (2) Feedforward ...
- Chameleon
# -*- coding: utf-8 -*- """ Created on Tue Dec 18 09:55:16 2018 @author: Mark,LI &quo ...
- EntityFramework Core
1,安装EF Core 在.csproj中添加一下配置,用于使用dotnet ef 命令 <ItemGroup> <DotNetCliToolReference Include=&q ...
- C# 基于 adb 控制安卓
我司呢,有深信服网关. 我们做项目,日常是比较“清闲”的,所以呢,深信服让你没有办法愉快的使用电脑, 但是手机是可以连外网和外面的 wifi 的,所以我们就玩手机,但是玩手机这么明显会被 Leader ...
- 使用TVTK库创建一个矩形视图
from tvtk.api import tvtk # s=tvtk.ConeSource(height=,radius=) # print(s.center) #创建一个长方体数据源,并同时设置长宽 ...
- CocosCreator的节点显示和隐藏
隐藏和显示有两种方式: 1.禁止节点node的运行,方法是x.node.active=false[此时隐藏了节点,且节点不再运行];恢复节点正常运行,x.node.active=true;或者使用x. ...
- NSArray与NSString、NSData,NSDictionary与NSString、NSData 相互转化
NSArray *array = @[ @1, @2, @3, @4, @5, @3 ]; // 1. NSArray与NSData相互转化 // NSArray to NSData NSError ...
- Mac下打开多个eclipse
命令行执行: open -n /Eclipse所在路径/Eclipse.app