All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tools.testng.TestngListener Maven / Gradle / Ivy

There is a newer version: 0.2.2
Show newest version
package tools.testng;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.*;
import org.testng.xml.XmlSuite;
import tools.annotation.ByteApiInfo;
import tools.assertion.AssertAssertion;
import tools.mapper.testng.TestngMapper;
import tools.service.MybatisService;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.SimpleDateFormat;
import java.util.*;

public class TestngListener extends TestListenerAdapter implements IInvokedMethodListener, IReporter {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestngListener.class);

    private TestngMapper testngMapper;

    /**** 源自 IInvokedMethodListener 接口 ****/
    @Override
    public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
        String methodName = iInvokedMethod.getTestMethod().getMethodName();
        String name = iTestResult.getName();
        if (methodName.contentEquals(name)){
            LOGGER.info(name+"开始执行。");
        }
    }

    @Override
    public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
        String methodName = iInvokedMethod.getTestMethod().getMethodName();
        String name = iTestResult.getName();
        if (methodName.contentEquals(name)){
            LOGGER.info(name+"执行完毕。");
        }
        LOGGER.info("测试结果是否成功:"+iTestResult.isSuccess());
        LOGGER.info("执行结果(1-成功,2-失败,3-skip):"+iTestResult.getStatus());
        LOGGER.info("用例名称:"+iTestResult.getName());
        LOGGER.info("测试结果:"+iTestResult.toString());
    }


    /**** 源自 TestListenerAdapter 类 ****/
    @Override
    public void onStart(ITestContext iTestContext) {
        super.onStart(iTestContext);
        LOGGER.info("测试 " + iTestContext.getName() + " 开始了。");
    }

    @Override
    public void onTestStart(ITestResult iTestResult) {
        super.onTestStart(iTestResult);
        LOGGER.info("用例 " + iTestResult.getName() + " 测试开始了。");
    }

    @Override
    public void onTestFailure(ITestResult iTestResult) {
        super.onTestFailure(iTestResult);
        LOGGER.info("用例 " + iTestResult.getName() + " 测试失败了。");
        LOGGER.info("当前测试一共失败了 "+ super.getFailedTests().size() + " 个。");
    }

    @Override
    public void onTestSuccess(ITestResult iTestResult) {
        super.onTestSuccess(iTestResult);
        LOGGER.info("用例 " + iTestResult.getName() + " 测试通过了。");
        LOGGER.info("当前测试一共通过了 "+ super.getPassedTests().size() + " 个。");
    }

    @Override
    public void onTestSkipped(ITestResult iTestResult) {
        super.onTestSkipped(iTestResult);
        LOGGER.info("用例 " + iTestResult.getName() + " 测试被跳过了。");
        LOGGER.info("当前测试一共跳过了 "+ super.getSkippedTests().size() + " 个。");
    }

    @Override
    public void onFinish(ITestContext iTestContext) {
        super.onFinish(iTestContext);
        LOGGER.info("测试 " + iTestContext.getName() + " 结束了。");
    }


    /**** 源自 IReporter 接口 ****/
    @Override
    public void generateReport(List xmlSuiteList, List iSuiteList, String outputDirectory) {
        LOGGER.info("*** generateReport Started ***");

        String uniqueTagForExtireTestExecution = System.getProperty("uniqueTag");
        if (uniqueTagForExtireTestExecution == null || "".contentEquals(uniqueTagForExtireTestExecution)){
            uniqueTagForExtireTestExecution = Long.toString(System.currentTimeMillis());
        }
        LOGGER.info("本轮测试唯一标识 = {}", uniqueTagForExtireTestExecution);
        String userDir = System.getProperty("user.dir");
        LOGGER.info("userDir = {}", userDir);
        String[] segmentArray = userDir.split("/");
        String projectName = segmentArray[segmentArray.length - 1];
        String absolutePath = userDir;
        if ("target".contentEquals(projectName)){
            projectName = segmentArray[segmentArray.length - 2];
            int indexOfEnd = userDir.lastIndexOf("/");
            absolutePath = userDir.substring(0, indexOfEnd);
        }
        LOGGER.info("项目名称 = "+projectName);
        LOGGER.info("项目绝对路径 = "+absolutePath);

        LOGGER.info("*** Start Assertion Info Retrieve ***");
        Map assertionMap = AssertAssertion.getTestAssertionInfoMap(absolutePath+"/src/test/java");
        LOGGER.info("assertionMap size = {}", assertionMap.size());
        LOGGER.info("*** Complete Assertion Info Retrieve ***");

        //用于保存本次测试的全部TestNG测试用例数据
        List testCaseList = new ArrayList<>();

        //用于保存本次测试的全部TestNG测试执行数据
        List statisticList = new ArrayList<>();

        String psmKey = System.getProperty("psm");
        LOGGER.info("psm={}", psmKey);
        int maxVersion = 0;

        /* 初始化Mybatis */
        MybatisService mybatisService = new MybatisService();
        try {
            this.testngMapper = mybatisService.initSqlSessionMapper(TestngMapper.class, true, "site_reldb");
            /* 使用Mybatis查询psm与testProject的用例最大version */
            Map maxVersionMap = testngMapper.selectMaxVersion(psmKey, projectName);
            if (maxVersionMap == null || maxVersionMap.isEmpty()){
                LOGGER.info("{}服务的测试项目{}是一个全新新项目。", psmKey, projectName);
            } else {
                maxVersion = Integer.parseInt(String.valueOf(maxVersionMap.get("MAX_VERSION")));
                LOGGER.info("测试起始的当前maxVersion={}", maxVersion);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        for(ISuite iSuite : iSuiteList){
            Map iSuiteResultMap = iSuite.getResults();
            List iInvokedMethodList = iSuite.getAllInvokedMethods();
            LOGGER.info("所有执行的方法共计 {} 个 :{}", iInvokedMethodList.size() , iInvokedMethodList);
            List iTestNGMethodList = iSuite.getAllMethods();
            LOGGER.info("所有@Test标注的方法共计 {} 个 :{}", iTestNGMethodList.size() , iTestNGMethodList);
            LOGGER.info("Suite名称:{}", iSuite.getName());
            LOGGER.info("输出路径:{}", iSuite.getOutputDirectory());
            LOGGER.info("并发方式:{}", iSuite.getParallel());
            String testerId = iSuite.getParameter("TESTER_ID");
            LOGGER.info("TESTER_ID:{}", testerId);
            LOGGER.info("报告路径:{}", outputDirectory);

            for(ISuiteResult iSuiteResult : iSuiteResultMap.values()){
                ITestContext iTestContext = iSuiteResult.getTestContext();
                LOGGER.info("Test名称 = {}", iTestContext.getName());
                IResultMap iPassedResultMap = iTestContext.getPassedTests();
                LOGGER.info("PassedTests 个数 = {}", iPassedResultMap.size());
                IResultMap iFailedResultMap = iTestContext.getFailedTests();
                LOGGER.info("FailedTests 个数 = {}", iFailedResultMap.size());
                IResultMap iSkippedResultMap = iTestContext.getSkippedTests();
                LOGGER.info("SkippedTests 个数 = {}", iSkippedResultMap.size());

                Set iPassedTestResultset = iPassedResultMap.getAllResults();
                for(ITestResult iPassedTestResult : iPassedTestResultset){
                    LOGGER.info("测试方法:{}", iPassedTestResult.getName());
                    LOGGER.info("执行结果(1-成功,2-失败,3-skip):{}", iPassedTestResult.getStatus());
                    testCaseList.add(storeCaseData(psmKey, projectName, maxVersion+1, iPassedTestResult));
                    statisticList.add(storeExecutionData(
                            uniqueTagForExtireTestExecution, projectName, iSuite, iTestContext,
                            iPassedTestResult,"PASSED", assertionMap
                    ));
                    /* 执行完DML语句后,进行提交,因为sqlSession默认的autocommit=false */
//                    mybatisService.getSqlSession().commit();
                }

                Set iFailedTestResultset = iFailedResultMap.getAllResults();
                for(ITestResult iFailedTestResult : iFailedTestResultset){
                    LOGGER.info("测试方法:{}", iFailedTestResult.getName());
                    LOGGER.info("执行结果(1-成功,2-失败,3-skip):{}", iFailedTestResult.getStatus());
                    testCaseList.add(storeCaseData(psmKey, projectName, maxVersion+1, iFailedTestResult));
                    statisticList.add(storeExecutionData(
                            uniqueTagForExtireTestExecution, projectName, iSuite, iTestContext,
                            iFailedTestResult,"FAILED", assertionMap
                    ));
                    /* 执行完DML语句后,进行提交,因为sqlSession默认的autocommit=false */
//                    mybatisService.getSqlSession().commit();
                }

                Set iSkippedTestResultset = iSkippedResultMap.getAllResults();
                for(ITestResult iSkippedTestResult : iSkippedTestResultset){
                    LOGGER.info("测试方法:{}", iSkippedTestResult.getName());
                    LOGGER.info("执行结果(1-成功,2-失败,3-skip):{}", iSkippedTestResult.getStatus());
                    testCaseList.add(storeCaseData(psmKey, projectName, maxVersion+1, iSkippedTestResult));
                    statisticList.add(storeExecutionData(
                            uniqueTagForExtireTestExecution, projectName, iSuite, iTestContext,
                            iSkippedTestResult,"SKIPPED", assertionMap
                    ));
                    /* 执行完DML语句后,进行提交,因为sqlSession默认的autocommit=false */
//                    mybatisService.getSqlSession().commit();
                }
            }
        }
        /* 使用Mybatis删除psm与testProject的原最大version的测试用例 */
        if (maxVersion > 0 && testngMapper != null){
            int delRows = testngMapper.deleteOldVersion(maxVersion, psmKey, projectName);
            LOGGER.info("{}服务、{}测试项目中的{}个用例被成功删除。", psmKey, projectName, delRows);
        }

        LOGGER.info("*** generateReport Completed ***");
    }

    /* 获取执行数据 */
    private Statistic storeExecutionData(String uniqueTag, String projectName, ISuite iSuite, ITestContext iTestContext, ITestResult iTestResult, String executionResult, Map assertionMap){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        String starTime = simpleDateFormat.format(iTestResult.getStartMillis());
        String finishTime = simpleDateFormat.format(iTestResult.getEndMillis());
        long duration = iTestResult.getEndMillis() - iTestResult.getStartMillis();
        LOGGER.info("开始时间:"+ starTime);
        LOGGER.info("结束时间:"+ finishTime);
        LOGGER.info("测试耗时:"+ duration + " 毫秒");

        Statistic statistic = new Statistic();
        statistic.setUniqueTag(uniqueTag);
        statistic.setProjectName(projectName);
        statistic.setSuiteName(iSuite.getName());
        statistic.setXmlTestName(iTestContext.getName());
        statistic.setClassName(iTestResult.getTestClass().getName()); // com.test.rest.MRestTest
        String currTestName = iTestResult.getName(); // testHttpByHttpClient1
        statistic.setTestName(currTestName);
        if (assertionMap != null && assertionMap.containsKey(currTestName)){
            statistic.setAssertion(Boolean.toString(assertionMap.get(currTestName)));
        } else if (assertionMap == null){
            LOGGER.error("[错误]:assertionMap为空。");
        } else {
            LOGGER.warn("[警告]:assertionMap中不包括测试用例 {} 。", currTestName);
        }

        /*获取测试用例@Test注解属性信息*/
        JSONObject testNGAnnoObject = new JSONObject(new LinkedHashMap<>());
        ITestNGMethod iTestNGMethod = iTestResult.getMethod();
        //与@Test注解无关,是每次执行一次Suite变化一次(一次执行,Suite里所有Test的ID都一样)。
        testNGAnnoObject.put("id", iTestNGMethod.getId());
        //下面是@Test注解的属性信息。
        testNGAnnoObject.put("priority", iTestNGMethod.getPriority());
        testNGAnnoObject.put("groups", String.join(",",iTestNGMethod.getGroups()));
        testNGAnnoObject.put("description", iTestNGMethod.getDescription());
        LOGGER.info("@Test注解的属性信息:{}", JSON.toJSONString(testNGAnnoObject, true));
        statistic.setTestAnnoInfo(controlStrLength(JSON.toJSONString(testNGAnnoObject), 512));

        /*获取测试用例的入参与预期值*/
        Map paramAndExpResMap = getParamAndExpRes(iTestResult);
        if (paramAndExpResMap == null){
            statistic.setTestParameters("NULL");
            statistic.setExpect("NULL");
        } else {
            statistic.setTestParameters(controlStrLength(paramAndExpResMap.get("Parameters"), 1024));
            statistic.setExpect(controlStrLength(paramAndExpResMap.get("Expect"), 1024));
        }

        /*获取测试用例的异常栈*/
        Throwable throwableObj = iTestResult.getThrowable();
        if ("FAILED".contentEquals(executionResult)){
            if (throwableObj != null){
                LOGGER.info("获取异常栈信息");
                statistic.setErrorMsg(throwableObj.getMessage());
                LOGGER.info("getMessage={}", throwableObj.getMessage());
                StringBuilder stackTraceBuilder = new StringBuilder();
                StackTraceElement[] eleArray = throwableObj.getStackTrace();
                for (int i=0 ; i < eleArray.length ; i++){
                    LOGGER.info(eleArray[i].toString());
                    if (i == eleArray.length-1){
                        stackTraceBuilder.append(eleArray[i]);
                    } else {
                        stackTraceBuilder.append(eleArray[i]).append("\n");
                    }
                }
                statistic.setStackTrace(controlStrLength(stackTraceBuilder.toString(),1024));

                LOGGER.info("************");
            }
        }

        /*获取被测接口信息*/
        Map annoInfoMap = retrieveByteApiInfo(iTestResult);
        if (annoInfoMap.size() > 0){
            String interName = "";
            String methodName = "";
            String interPath = "";
            ProtocolTypeEnum protocolType = ProtocolTypeEnum.HTTP;
            if (annoInfoMap.containsKey("interName")){
                interName = annoInfoMap.get("interName");
                statistic.setInterName(interName);
            }
            if (annoInfoMap.containsKey("methodName")){
                methodName = annoInfoMap.get("methodName");
                statistic.setMethodName(methodName);
            }
            if (annoInfoMap.containsKey("interPath")){
                interPath = annoInfoMap.get("interPath");
                statistic.setInterPath(interPath);
            }
            if (annoInfoMap.containsKey("protocolType")){
                protocolType = ProtocolTypeEnum.valueOf(annoInfoMap.get("protocolType"));
                statistic.setProtocolType(protocolType);
            }

            /* 下面是额外获取被测接口参数情况 */
            if (protocolType != ProtocolTypeEnum.HTTP && !("".contentEquals(interPath) || "".contentEquals(interName))){
                try {
                    Class interClass = Class.forName(interPath+"."+interName);
                    Method[] declaredMethods = interClass.getDeclaredMethods();
                    if (declaredMethods != null && declaredMethods.length > 0){
                        for (Method method : declaredMethods){
                            if (methodName != null && methodName.contentEquals(method.getName())){
                                Parameter[] parameters = method.getParameters();
                                if (parameters != null && parameters.length > 0){
                                    JSONObject methodParamsJSONObject = new JSONObject(new LinkedHashMap<>());
                                    for (Parameter parameter : parameters){
                                        methodParamsJSONObject.put(parameter.getName(), parameter.getType().getTypeName());
                                    }
                                    statistic.setMethodParameters(controlStrLength(JSON.toJSONString(methodParamsJSONObject), 1024));
                                    LOGGER.info("被测接口的参数情况:{}", JSON.toJSONString(methodParamsJSONObject, true));
                                }
                                break;
                            }
                        }
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

        statistic.setTestResult(executionResult);
        statistic.setDuration(Long.toString(duration));
        statistic.setStartTime(starTime);
        statistic.setFinishTime(finishTime);

        LOGGER.info(JSON.toJSONString(statistic, true));
        /* 使用Mybatis入库 */
        if (testngMapper != null){
            testngMapper.addTestExecutionRecord(statistic);
            LOGGER.info("测试执行记录入库。");
        } else {
            LOGGER.info("testngMapper is null");
        }

        return statistic;
    }

    /* 获取用例数据 */
    private TestCase storeCaseData(String psmKey, String projectName, int newVersion, ITestResult iTestResult){

        TestCase testCase = new TestCase();
        testCase.setProjectName(projectName);
        testCase.setPsmKey(psmKey);
        testCase.setTestClass(iTestResult.getTestClass().getName()); // com.test.rest.MRestTest
        testCase.setTestMethod(iTestResult.getName()); // testHttpByHttpClient1
        testCase.setVersion(newVersion); //已经是maxVersion+1的值。

        /*获取测试用例@Test注解属性信息*/
        JSONObject testNGAnnoObject = new JSONObject(new LinkedHashMap<>());
        ITestNGMethod iTestNGMethod = iTestResult.getMethod();
        //与@Test注解无关,是每次执行一次Suite变化一次(一次执行,Suite里所有Test的ID都一样)。
        testNGAnnoObject.put("id", iTestNGMethod.getId());
        //下面是@Test注解的属性信息。
        testNGAnnoObject.put("priority", iTestNGMethod.getPriority());
        testNGAnnoObject.put("groups", String.join(",",iTestNGMethod.getGroups()));
        String testDesc = iTestNGMethod.getDescription();
        testNGAnnoObject.put("description", testDesc);
        LOGGER.info("@Test注解的属性信息:{}", JSON.toJSONString(testNGAnnoObject, true));
        testCase.setTestDesc(testDesc);

        /*获取被测接口信息*/
        Map annoInfoMap = retrieveByteApiInfo(iTestResult);
        System.out.println("annoInfoMap="+annoInfoMap);
        if (!annoInfoMap.isEmpty()){
            String interName = "";
            String methodName = "";
            String interPath = "";
            if (annoInfoMap.containsKey("protocolType")){
                ProtocolTypeEnum protocolType = ProtocolTypeEnum.valueOf(annoInfoMap.get("protocolType"));
                testCase.setProtocolType(protocolType);
                if (protocolType == ProtocolTypeEnum.HTTP && annoInfoMap.containsKey("interPath")){
                    interPath = annoInfoMap.get("interPath");
                    testCase.setInterName(interPath);
                }
                if (protocolType == ProtocolTypeEnum.THRIFT && annoInfoMap.containsKey("interName") && annoInfoMap.containsKey("methodName")){
                    interName = annoInfoMap.get("interName");
                    methodName = annoInfoMap.get("methodName");
                    testCase.setInterName(interName+"."+methodName);
                }
            } else {
                LOGGER.error("[ERROR]: annoInfoMap中缺少protocolType key。");
            }
            LOGGER.info(JSON.toJSONString(testCase, true));
            /* 使用Mybatis入库 */
            if (testngMapper != null){
                try{
                    Map existTestCaseMap = testngMapper.selectExistTestCase(testCase);
                    if (existTestCaseMap == null || existTestCaseMap.isEmpty()){
                        testngMapper.addTestCaseRecord(testCase);
                        LOGGER.info("测试用例记录入库。");
                    } else {
                        LOGGER.info("当前已存在[Test Case]: {}", testCase);
                        testngMapper.updateVersion(testCase);
                        LOGGER.info("更新用例版本成功。");
                    }
                }catch (org.apache.ibatis.exceptions.TooManyResultsException e){
                    LOGGER.error("[ERROR]: 用例:{} 在数据库中存在多个一模一样的记录。", testCase);
                    LOGGER.error(e.getMessage(), e);
                    return null;
                }
            } else {
                LOGGER.info("testngMapper is null");
            }
        }

        return testCase;
    }


    private Map retrieveByteApiInfo(ITestResult iTestResult) {
        Map annoInfoMap = new HashMap<>();
        ITestNGMethod iTestNGMethod = iTestResult.getMethod();
        Method currTestMethod = iTestNGMethod.getConstructorOrMethod().getMethod();
        Method currRealMethod = null;
        try {
            currRealMethod = currTestMethod.getDeclaringClass().getDeclaredMethod(currTestMethod.getName(), currTestMethod.getParameterTypes());
        } catch (NoSuchMethodException e) {
            LOGGER.error(e.getMessage(), e);
        }
        if(currRealMethod.isAnnotationPresent(ByteApiInfo.class)) {
            LOGGER.info("[有] 测试用例 {} 被 ByteApiInfo 注解修饰。", currRealMethod);
            ByteApiInfo ByteApiInfoAnno = currRealMethod.getAnnotation(ByteApiInfo.class);
            annoInfoMap.put("interName", ByteApiInfoAnno.interName());
            annoInfoMap.put("methodName", ByteApiInfoAnno.methodName());
            annoInfoMap.put("interPath", ByteApiInfoAnno.interPath());
            annoInfoMap.put("protocolType", ByteApiInfoAnno.protocolType().name());
            LOGGER.info("被测 {} 接口为:{} --- 被测方法为:{} --- 被测接口包路径为:{}", ByteApiInfoAnno.protocolType(), ByteApiInfoAnno.interName(), ByteApiInfoAnno.methodName(), ByteApiInfoAnno.interPath());
        } else {
            LOGGER.info("[未] 测试用例 {} 未被 ByteApiInfo 注解修饰。", iTestResult.getName());
        }
        return annoInfoMap;
    }

    private Map getParamAndExpRes(ITestResult iTestResult){
        Object[] paramArray = iTestResult.getParameters();
        JSONObject paramJSONObject = new JSONObject(true);
        JSONObject expJSONObject = new JSONObject(new LinkedHashMap<>());
        if (paramArray.length == 0){
            LOGGER.info("测试用例[{}], 无入参。", iTestResult.getName());
            return null;
        } else {
            for (int i=0; i < paramArray.length; i++){
                if (paramArray[i] instanceof JSONObject){
                    JSONObject jsonObjectParam = (JSONObject) paramArray[i];
                    if (jsonObjectParam.containsKey("returnJSONObject")){
                        expJSONObject = jsonObjectParam.getJSONObject("returnJSONObject");
                    } else {
                        paramJSONObject.put("Parameter"+i, jsonObjectParam);
                    }
                } else {
                    String paramTypeName = paramArray[i].getClass().getTypeName();
                    if (paramTypeName.startsWith("java.") || paramTypeName.startsWith("javax.")){
                        paramJSONObject.put("Parameter"+i, paramArray[i]);
                    } else {
                        paramJSONObject.put("Parameter"+i, JSON.toJSONString(paramArray[i], true));
                    }
                }
            }
            Map paramAndExpResMap = new HashMap<>();
            LOGGER.info("打印入参:{}", JSON.toJSONString(paramJSONObject, true));
            paramAndExpResMap.put("Parameters", JSON.toJSONString(paramJSONObject));
            LOGGER.info("打印预期结果:{}", JSON.toJSONString(expJSONObject, true));
            if (expJSONObject.size() == 0){
                paramAndExpResMap.put("Expect", "NULL");
            } else {
                paramAndExpResMap.put("Expect", JSON.toJSONString(expJSONObject));
            }
            return paramAndExpResMap;
        }
    }

    private String controlStrLength(String inputStr, int maxLength){
        if (inputStr.length() > maxLength){
            return inputStr.substring(0, maxLength);
        } else {
            return inputStr;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy