通过IMethodInterceptor动态设置@test方法的执行顺序

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进行执行,具体什么原因?我们需要翻看下源码查看其具体的排序规则是什么


image.png

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这个属性都没有,尝试了下发现没有问题;后面再研究下为什么会源码为什么要那么实现

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容