tools.testng.TestngListener Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-autotest-tool Show documentation
Show all versions of java-autotest-tool Show documentation
This is an integration of autotest tools
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;
}
}
}