IMethodInterceptor监听器可以在TestNg 开始执行前处理测试方法,但只有不依赖于其他方法的测试方法才会被作为参数传递,该接口的实现类,需要返回IMethodInstance的list集合用于后续TestNg去执行
public interface IMethodInterceptor extends ITestNGListener {
List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context);
}
MethodInterceptor具体实现
package testNg.listeners;
import database.MysqlConn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.annotations.Test;
import testNg.dataprovider.DataProviderForDB;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class MethodInterceptor implements IMethodInterceptor {
List<IMethodInstance> returnMethod = new ArrayList<>();
private static final Logger logger = LogManager.getLogger(MethodInterceptor.class);
@Override
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
// IMethodInstance 这个接口所返回的是所有测试方法或实例(所有方法都在里面)
// 将IMethodInstance对象的list 创建为迭代器
Iterator<IMethodInstance> tie = methods.iterator();
// 遍历
while (tie.hasNext()) {
IMethodInstance method = tie.next();
// 带注释的TestNG方法
ITestNGMethod x = method.getMethod();
// 判断这个方法时@注释描述的
if (x.isTest()) {
String methodname = x.getMethodName();
String sql = "select * from test_data where method = '" + methodname + "' limit 1";
List<Map<String, Object>> list = MysqlConn.getResults(sql);
try {
if (list.size() > 0) {
Map<String, Object> map = list.get(0);
x.setPriority((Integer) map.get("priority"));//动态设置方法的执行顺序
logger.info("设置"+methodname+"优先级为"+map.get("priority"));
}
returnMethod.add(method);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// return
return returnMethod;
}
}
测试用例
package testNg.test.course;
import httprequest.HttpRequest;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import testNg.dataprovider.DataProviderMethod;
import testNg.listeners.MethodInterceptor;
import java.util.Map;
@Listeners(MethodInterceptor.class)//加入监听器
public class TeamCourseTest extends DataProviderMethod {
String sql = "select * from test_data where className = 'course.TeamCourseTest'";
public TeamCourseTest(){
super.sql = sql;
}
@Test(dataProvider = "testdata")
public void createTeamCourseSchedule(Map<String,String> data){
HttpRequest.execRequest(data);
}
@Test(dataProvider = "testdata")
public void createTeamCourseReserve(Map<String,String> data){
HttpRequest.execRequest(data);
}
@Test(dataProvider = "testdata")
public void cancelTeamCourseReserve(Map<String,String> data){
HttpRequest.execRequest(data);
}
}
此时我们在执行的时候,发现还是并没有按照我们设置的priority进行执行,具体什么原因?我们需要翻看下源码查看其具体的排序规则是什么
testNg的test方法执行方法入口TestRunner.privateRun(),freeNodes.sort(methodComparator)主要是去对执行方法进行排序
private void privateRun(XmlTest xmlTest) {
List<ITestNGMethod> freeNodes = graph.getFreeNodes();
//这个是处理监听器中返回的方法的
ITestNGMethod[] interceptedOrder = intercept(m_allTestMethods);
if (graph.getNodeCount() > 0 && freeNodes.isEmpty()) {
throw new TestNGException("No free nodes found in:" + graph);
}
while (!freeNodes.isEmpty()) {
if (needPrioritySort) {
freeNodes.sort(methodComparator);//这里主要是对需要执行的测试方法的排序
freeNodes = freeNodes.subList(0, 1);
}
createWorkers(freeNodes).forEach(Runnable::run);
graph.setStatus(freeNodes, IDynamicGraph.Status.FINISHED);
freeNodes = graph.getFreeNodes();
}
}
TestMethodComparator主要是通过实现Comparator接口对测试方法进行排序,从compareStatic 这个静态方法可以看到在判断Priority这个属性值之前,还对InterceptedPriority这个值的大小进行了判断,这里可以猜测可能是这个值影响了执行顺序,但我并没有设置这个值,肯定是源码哪里自己做了设置,继续回到TestRunner类中
public class TestMethodComparator implements Comparator<ITestNGMethod> {
@Override
public int compare(ITestNGMethod o1, ITestNGMethod o2) {
return compareStatic(o1, o2);
}
public static int compareStatic(ITestNGMethod o1, ITestNGMethod o2) {
int prePriDiff = Integer.compare(o1.getInterceptedPriority(), o2.getInterceptedPriority());
if (prePriDiff != 0) {
return prePriDiff;
}
int priDiff = Integer.compare(o1.getPriority(), o2.getPriority());
if (priDiff != 0) {
return priDiff;
}
return o1.getMethodName().compareTo(o2.getMethodName());
}
}
回到intercept方法,我们发现在结尾的地方对testMethod实例的InterceptedPriority属性值进行了赋值,从代码可以知道只要监听器大于1个,就会执行该方法,那么也就意味着肯定会造成InterceptedPriority不一致,这样肯定不能达到我们的目的了,虽然不知道为什么会这么做,现在想要解决该问题,要不然就是改源码,去掉对InterceptedPriority的判断,要不然看看TestNg的其他版本
/** Apply the method interceptor (if applicable) to the list of methods. */
private ITestNGMethod[] intercept(ITestNGMethod[] methods) {
List<IMethodInstance> methodInstances =
MethodHelper.methodsToMethodInstances(Arrays.asList(methods));
for (IMethodInterceptor m_methodInterceptor : m_methodInterceptors) {
methodInstances = m_methodInterceptor.intercept(methodInstances, this);
}
List<ITestNGMethod> result = MethodHelper.methodInstancesToMethods(methodInstances);
// If the user specified a method interceptor, whatever that returns is the order we're going
// to run things in. Set the intercepted priority for that case.
// There's a built-in interceptor, so look for more than one.
if (m_methodInterceptors.size() > 1) {
for (int i = 0; i < resultArray.length; ++i) {
resultArray[i].setInterceptedPriority(i);
}
}
最后还是懒得改了,我现在用的是7.0.0的版本,我找了下6.14.3版本的源码,发现里面压根连InterceptedPriority这个属性都没有,尝试了下发现没有问题;后面再研究下为什么会源码为什么要那么实现