/**
* 报表信息
* @author liwen406
* @date 2019-09-25 11:02
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class ReportTotal {
private Integer id;
/**
* 开始时间
*/
private String testUser;
/**
* 结束时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
/**
* 合计运行时间
*/
private String runTime;
/**
* 运行版本
*/
private String runVersion;
/**
* 系统版本
*/
private String sysVersion;
/**
* 设备型号(手机)
*/
private String typeInfo;
/**
* 用例总数
*/
private String total;
/**
* 成功数据
*/
private String successTotal;
/**
* 失败数
*/
private String failedTotal;
/**
* 跳过
*/
private String skippedTotal;
/**
* 错误数
*/
private String errorTotal;
/**
* 执行执行方法
*/
private String runMethod;
/**
* 状态
*/
private String status;
/**
* 详情
*/
private String descriptionInfo;
/**
* 持续时间
*/
private String duration;
/**
* 日志
*/
private String detail;
/**
* @author liwen406
* @Title: ReportUtil
* @Description: 报表工具类
* @date 2019/9/19 / 17:34
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class ReportUtil {
private static final NumberFormat DURATION_FORMAT = new DecimalFormat("#0.000");
private static final NumberFormat PERCENTAGE_FORMAT = new DecimalFormat("#0.00%");
public static String formatDate(long date) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return formatter.format(date);
}
/**
* 测试消耗时长
* return 秒,保留3位小数
*/
public String getTestDuration(ITestContext context) {
long duration;
duration = context.getEndDate().getTime() - context.getStartDate().getTime();
return formatDuration(duration);
}
public static String formatDuration(long elapsed) {
double seconds = (double) elapsed / 1000;
return DURATION_FORMAT.format(seconds);
}
/**
* 测试通过率
* return 2.22%,保留2位小数
*/
public String formatPercentage(int numerator, int denominator) {
return PERCENTAGE_FORMAT.format(numerator / (double) denominator);
}
/**
* 获取方法参数,以逗号分隔
*
* @param result
* @return
*/
public static String getParams(ITestResult result) {
Object[] params = result.getParameters();
List<String> list = new ArrayList<String>(params.length);
for (Object o : params) {
list.add(renderArgument(o));
}
return commaSeparate(list);
}
/**
* 获取依赖的方法
*
* @param result
* @return
*/
public String getDependMethods(ITestResult result) {
String[] methods = result.getMethod().getMethodsDependedUpon();
return commaSeparate(Arrays.asList(methods));
}
/**
* 堆栈轨迹,暂不确定怎么做,放着先
*
* @param throwable
* @return
*/
public String getCause(Throwable throwable) {
StackTraceElement[] stackTrace = throwable.getStackTrace(); //堆栈轨迹
List<String> list = new ArrayList<String>(stackTrace.length);
for (Object o : stackTrace) {
list.add(renderArgument(o));
}
return commaSeparate(list);
}
/**
* 获取全部日志输出信息
*
* @return
*/
public List<String> getAllOutput() {
return Reporter.getOutput();
}
/**
* 按testresult获取日志输出信息
*
* @param result
* @return
*/
public List<String> getTestOutput(ITestResult result) {
return Reporter.getOutput(result);
}
/*将object 转换为String*/
private static String renderArgument(Object argument) {
if (argument == null) {
return "null";
} else if (argument instanceof String) {
return "\"" + argument + "\"";
} else if (argument instanceof Character) {
return "\'" + argument + "\'";
} else {
return argument.toString();
}
}
/*将集合转换为以逗号分隔的字符串*/
private static String commaSeparate(Collection<String> strings) {
StringBuilder buffer = new StringBuilder();
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()) {
String string = iterator.next();
buffer.append(string);
if (iterator.hasNext()) {
buffer.append(", ");
}
}
return buffer.toString();
}
/**
* @author liwen406
* @Title: TestResult
* @Description: 用于存储测试结果
* @date 2019/9/19 / 17:28
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class TestResult {
//测试方法名
private String testName;
//测试类名
private String className;
private String caseName;
//测试用参数
private String params;
//测试描述
private String description;
//Reporter Output
private String output;
private List<String> twooutparam ;
//测试异常原因
private Throwable throwable;
private String throwableTrace;
//状态
private int status;
private String duration;
private boolean success;
/**
* @author liwen406
* @Title: TestResultCollection
* @Description: testng采用数据驱动,一个测试类可以有多个测试用例集合,每个测试类,应该有个测试结果集
* @date 2019/9/19 / 17:31
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class TestResultCollection {
private int totalSize = 0;
private int successSize = 0;
private int failedSize = 0;
private int errorSize = 0;
private int skippedSize = 0;
private List<TestResult> resultList;
public void addTestResult(TestResult result) {
if (resultList == null) {
resultList = new LinkedList<>();
}
resultList.add(result);
switch (result.getStatus()) {
case ITestResult.FAILURE:
failedSize += 1;
break;
case ITestResult.SUCCESS:
successSize += 1;
break;
case ITestResult.SKIP:
skippedSize += 1;
break;
}
totalSize += 1;
}
ReporterListener.class
import cases.startdemo.utils.SendMailUtils;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.jd.dhf.api.test.util.LogUtil;
import com.jd.fastjson.JSON;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import jdth.getsku.dao.imp.ReportUiDaoImpl;
import jdth.getsku.pojo.ReportUi;
import jdth.getsku.util.Log;
import jdth.getsku.util.OperationalCmd;
import jdth.getsku.util.WaitUtil;
import jdth.global.pagatest.BestRuner;
import org.apache.commons.io.FileUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.openqa.selenium.OutputType;
import org.testng.*;
import org.testng.annotations.Test;
import org.testng.xml.XmlSuite;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author liwen
* @Title: ReporterListener
* @Description: 监听类 UI自动化测试报告
* @date 2019/9/19 / 17:33
*/
public class ReporterListener implements IReporter, ITestListener {
DateFormat dateFormat = new SimpleDateFormat("yyyy_MMdd_hhmmss");
/**
* 保存数据
*/
ReportUiDaoImpl reportUiDao = new ReportUiDaoImpl();
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
List<ITestResult> list = new LinkedList<>();
Date startDate = new Date();
Date endDate = new Date();
int TOTAL = 0;
int SUCCESS = 1;
int FAILED = 0;
int ERROR = 0;
int SKIPPED = 0;
for (ISuite suite : suites) {
Map<String, ISuiteResult> suiteResults = suite.getResults();
for (ISuiteResult suiteResult : suiteResults.values()) {
ITestContext testContext = suiteResult.getTestContext();
startDate = startDate.getTime() > testContext.getStartDate().getTime() ? testContext.getStartDate() : startDate;
if (endDate == null) {
endDate = testContext.getEndDate();
} else {
endDate = endDate.getTime() < testContext.getEndDate().getTime() ? testContext.getEndDate() : endDate;
}
IResultMap passedTests = testContext.getPassedTests();
IResultMap failedTests = testContext.getFailedTests();
IResultMap skippedTests = testContext.getSkippedTests();
IResultMap failedConfig = testContext.getFailedConfigurations();
SUCCESS += passedTests.size();
FAILED += failedTests.size();
SKIPPED += skippedTests.size();
ERROR += failedConfig.size();
list.addAll(this.listTestResult(passedTests));
list.addAll(this.listTestResult(failedTests));
list.addAll(this.listTestResult(skippedTests));
list.addAll(this.listTestResult(failedConfig));
}
}
/* 计算总数 */
TOTAL = SUCCESS + FAILED + SKIPPED + ERROR;
this.sort(list);
Map<String, TestResultCollection> collections = this.parse(list);
VelocityContext context = new VelocityContext();
context.put("TOTAL", TOTAL);
context.put("mobileModel", OperationalCmd.getMobileModel());
context.put("versionName", OperationalCmd.getVersionNameInfo());
context.put("SUCCESS", SUCCESS);
context.put("FAILED", FAILED);
context.put("ERROR", ERROR);
context.put("SKIPPED", SKIPPED);
context.put("startTime", ReportUtil.formatDate(startDate.getTime()) + "<--->" + ReportUtil.formatDate(endDate.getTime()));
context.put("DURATION", ReportUtil.formatDuration(endDate.getTime() - startDate.getTime()));
context.put("results", collections);
write(context, outputDirectory);
//通过post请求
ReportTotal reportTotal = new ReportTotal();
reportTotal.setTestUser(System.getProperty("user.name"));
reportTotal.setStartTime(startDate + "");
reportTotal.setEndTime(endDate + "");
reportTotal.setRunTime(ReportUtil.formatDuration(endDate.getTime() - startDate.getTime()));
reportTotal.setRunVersion(OperationalCmd.getVersionNameInfo());
reportTotal.setSysVersion(OperationalCmd.getVersionNameInfo());
reportTotal.setTypeInfo(OperationalCmd.getMobileModel());
reportTotal.setTotal(TOTAL + "");
reportTotal.setSuccessTotal(SUCCESS + "");
reportTotal.setFailedTotal(FAILED + "");
reportTotal.setSkippedTotal(SKIPPED + "");
// String s = JSONObject.toJSONString(reportTotal);
// HttpRequest.post("http://127.0.0.1:8081/report/inserttotal").body(s).execute().body();
}
/**
* 输出模板
*
* @param context
* @param outputDirectory
*/
private void write(VelocityContext context, String outputDirectory) {
String fileDir = ReporterListener.class.getResource("/Template").getPath();
String reslutpath = outputDirectory + "/html/report" + dateFormat.format(new Date()) + ".html";
try {
//写文件
VelocityEngine ve = new VelocityEngine();
Properties p = new Properties();
p.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, fileDir);
p.setProperty(Velocity.ENCODING_DEFAULT, "utf-8");
p.setProperty(Velocity.INPUT_ENCODING, "utf-8");
ve.init(p);
Template t = ve.getTemplate("report.vm");
//输出结果
OutputStream out = new FileOutputStream(new File(reslutpath));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
// 转换输出
t.merge(context, writer);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
/**
* 发送邮件
*/
// SendMailUtils.sendemali(reslutpath);
}
private void sort(List<ITestResult> list) {
Collections.sort(list, new Comparator<ITestResult>() {
@Override
public int compare(ITestResult r1, ITestResult r2) {
if (r1.getStatus() < r2.getStatus()) {
return 1;
} else {
return -1;
}
}
});
}
private LinkedList<ITestResult> listTestResult(IResultMap resultMap) {
Set<ITestResult> results = resultMap.getAllResults();
return new LinkedList<>(results);
}
private Map<String, TestResultCollection> parse(List<ITestResult> list) {
Map<String, TestResultCollection> collectionMap = new HashMap<>();
for (ITestResult t : list) {
String className = t.getTestClass().getName();
if (collectionMap.containsKey(className)) {
TestResultCollection collection = collectionMap.get(className);
collection.addTestResult(toTestResult(t));
} else {
TestResultCollection collection = new TestResultCollection();
collection.addTestResult(toTestResult(t));
collectionMap.put(className, collection);
}
}
return collectionMap;
}
private TestResult toTestResult(ITestResult t) {
TestResult testResult = new TestResult();
// Object[] params = t.getParameters();
// testResult.setParams(params + "");
// if (params != null && params.length >= 1) {
// String caseId = (String) params[0];
// LogUtil.info("caseid" + caseId);
// testResult.setCaseName(caseId);
// } else {
// testResult.setCaseName("null");
// }
testResult.setTestName(t.getMethod().getMethodName());
testResult.setParams(System.getProperty("user.name"));
testResult.setClassName(t.getTestClass().getName());
// testResult.setParams(ReportUtil.getParams(t));
//获取注解上面的 testName
testResult.setCaseName(t.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).testName());
testResult.setDescription(t.getMethod().getDescription());
testResult.setStatus(t.getStatus());
testResult.setThrowableTrace("class: " + t.getTestClass().getName() + " <br/> method: " + t.getName() + " <br/> error: " + t.getThrowable());
testResult.setThrowable(t.getThrowable());
long duration = t.getEndMillis() - t.getStartMillis();
testResult.setDuration(ReportUtil.formatDuration(duration));
testResult.setTwooutparam(Reporter.getOutput(t));
List<String> output = Reporter.getOutput(t);
StringBuffer stringBuffer = new StringBuffer();
for (String s : output) {
stringBuffer.append(s + ",");
}
testResult.setOutput(stringBuffer.toString());
// String s = JSONObject.toJSONString(testResult);
// HttpRequest.post("http://127.0.0.1:8081/report/insertreport").body(s).execute().body();
return testResult;
}
/**
* 每次调用测试@Test之前调用
*
* @param result
*/
@Override
public void onTestStart(ITestResult result) {
logTestStart(result);
}
/**
* 用例执行结束后,用例执行成功时调用
*
* @param result
*/
@Override
public void onTestSuccess(ITestResult result) {
ReportUi reportUi = new ReportUi();
//开始时间
reportUi.setStartTime(ReportUtil.formatDate(result.getStartMillis()));
reportUi.setStatus("1");
//结束时间
reportUi.setEndtime(ReportUtil.formatDate(result.getEndMillis()));
//成功数据
reportUi.setSuccess("成功");
//运行用例类名字
reportUi.setRunMethodName(result.getName());
//描述
reportUi.setDescriptionInfo(result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).description());
reportUi.setTypeInfo(result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).testName());
LogUtil.info("查看是否获取数据:" + result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).testName());
//持续时间
reportUi.setDuration(String.valueOf(result.getEndMillis() - result.getStartMillis()) + "毫秒");
//日志
List<String> output = Reporter.getOutput(result);
StringBuffer stringBuffer = new StringBuffer();
for (String s : output) {
stringBuffer.append(s + "<br/>");
}
reportUi.setDetail(stringBuffer.toString());
reportUiDao.savereport(reportUi);
logTestEnd(result, "Success");
}
/**
* 用例执行结束后,用例执行失败时调用
* 跑fail则截图 获取屏幕截图
*
* @param result
*/
@Override
public void onTestFailure(ITestResult result) {
WaitUtil.sleep(2000);
AndroidDriver<AndroidElement> driver = BestRuner.getDriver();
File srcFile = driver.getScreenshotAs(OutputType.FILE);
File location = new File("./test-output/html/result/screenshots");
if (!location.exists()) {
location.mkdirs();
}
String dest = result.getMethod().getRealClass().getSimpleName() + "." + result.getMethod().getMethodName();
String s = dest + "_" + dateFormat.format(new Date()) + ".png";
File targetFile =
new File(location + "/" + s);
LogUtil.info("截图位置:");
Reporter.log("<font color=\"#FF0000\">截图位置</font><br /> " + targetFile.getPath());
LogUtil.info("------file is ---- " + targetFile.getPath());
try {
FileUtils.copyFile(srcFile, targetFile);
} catch (IOException e) {
e.printStackTrace();
}
logTestEnd(result, "Failed");
//报告截图后面显示
Reporter.log("<img src=\"./result/screenshots/" + s + "\" width=\"64\" height=\"64\" alt=\"***\" onMouseover=\"this.width=353; this.height=613\" onMouseout=\"this.width=64;this.height=64\" />");
ReportUi reportUi = new ReportUi();
//开始时间
reportUi.setStartTime(ReportUtil.formatDate(result.getStartMillis()));
//结束时间
reportUi.setEndtime(ReportUtil.formatDate(result.getEndMillis()));
reportUi.setStatus("0");
//成功数据
reportUi.setFailed("失败");
//运行用例类名字
reportUi.setRunMethodName(result.getName());
//描述
reportUi.setDescriptionInfo(result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).description());
reportUi.setTypeInfo(result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).testName());
//持续时间
reportUi.setDuration(String.valueOf(result.getEndMillis() - result.getStartMillis()) + "毫秒");
//日志
List<String> output = Reporter.getOutput(result);
StringBuffer stringBuffer = new StringBuffer();
for (String s1 : output) {
stringBuffer.append(s1 + "<br/>");
}
stringBuffer.append("+截图路径:" + targetFile.getPath());
reportUi.setDetail(stringBuffer.toString());
reportUiDao.savereport(reportUi);
}
/**
* 用例执行结束后,用例执行skip时调用
*
* @param result
*/
@Override
public void onTestSkipped(ITestResult result) {
ReportUi reportUi = new ReportUi();
//开始时间
reportUi.setStartTime(ReportUtil.formatDate(result.getStartMillis()));
//结束时间
reportUi.setEndtime(ReportUtil.formatDate(result.getEndMillis()));
reportUi.setStatus("2");
//成功数据
reportUi.setSkipped("跳过");
//运行用例类名字
reportUi.setRunMethodName(result.getName());
//描述
reportUi.setDescriptionInfo(result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).description());
reportUi.setTypeInfo(result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).testName());
//持续时间
reportUi.setDuration(String.valueOf(result.getEndMillis() - result.getStartMillis()) + "毫秒");
//日志
List<String> output = Reporter.getOutput(result);
StringBuffer stringBuffer = new StringBuffer();
for (String s : output) {
stringBuffer.append(s + "<br/>");
}
reportUi.setDetail(stringBuffer.toString());
reportUiDao.savereport(reportUi);
logTestEnd(result, "Skipped");
}
/**
* 每次方法失败但是已经使用successPercentage进行注释时调用,并且此失败仍保留在请求的成功百分比之内。
*
* @param result
*/
@Override
public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
logTestEnd(result, "FailedButWithinSuccessPercentage");
}
/**
* 在测试类被实例化之后调用,并在调用任何配置方法之前调用。
*
* @param context
*/
@Override
public void onStart(ITestContext context) {
return;
}
/**
* 在所有测试运行之后调用,并且所有的配置方法都被调用
*
* @param context
*/
@Override
public void onFinish(ITestContext context) {
return;
}
/**
* 在用例执行结束时,打印用例的执行结果信息
*/
protected void logTestEnd(ITestResult tr, String result) {
Reporter.log(String.format("=============Result: %s=============", result), true);
}
/**
* 在用例开始时,打印用例的一些信息,比如@Test对应的方法名,用例的描述等等
*/
protected void logTestStart(ITestResult tr) {
Reporter.log(String.format("=============Run: %s===============", tr.getMethod().getMethodName()), true);
Reporter.log(String.format("用例描述: %s, 优先级: %s", tr.getMethod().getDescription(), tr.getMethod().getPriority()),
true);
return;
}
}
注意存放路径
report.vm
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UI自动</title>
<style>
body {
background-color: #f2f2f2;
color: #333;
margin: 0 auto;
width: 960px;
}
#summary {
width: 960px;
margin-bottom: 20px;
}
#summary th {
background-color: skyblue;
padding: 5px 12px;
}
#summary td {
background-color: lightblue;
text-align: center;
padding: 4px 8px;
}
.details {
width: 960px;
margin-bottom: 20px;
}
.details th {
background-color: skyblue;
padding: 5px 12px;
}
.details tr .passed {
background-color: lightgreen;
}
.details tr .failed {
background-color: red;
}
.details tr .unchecked {
background-color: gray;
}
.details td {
background-color: lightblue;
padding: 5px 12px;
}
.details .detail {
background-color: lightgrey;
font-size: smaller;
padding: 5px 10px;
text-align: center;
}
.details .success {
background-color: greenyellow;
}
.details .error {
background-color: red;
}
.details .failure {
background-color: salmon;
}
.details .skipped {
background-color: gray;
}
.button {
font-size: 1em;
padding: 6px;
width: 4em;
text-align: center;
background-color: #06d85f;
border-radius: 20px/50px;
cursor: pointer;
transition: all 0.3s ease-out;
}
a.button {
color: gray;
text-decoration: none;
}
.button:hover {
background: #2cffbd;
}
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 500ms;
visibility: hidden;
opacity: 0;
}
.overlay:target {
visibility: visible;
opacity: 1;
}
.popup {
margin: 70px auto;
padding: 20px;
background: #fff;
border-radius: 10px;
width: 50%;
position: relative;
transition: all 3s ease-in-out;
}
.popup h3 {
margin-top: 0;
color: #333;
font-family: Tahoma, Arial, sans-serif;
}
.popup .close {
position: absolute;
top: 20px;
right: 30px;
transition: all 200ms;
font-size: 30px;
font-weight: bold;
text-decoration: none;
color: #333;
}
.popup .close:hover {
color: #06d85f;
}
.popup .content {
max-height: 80%;
overflow: auto;
text-align: left;
}
@media screen and (max-width: 700px) {
.box {
width: 70%;
}
.popup {
width: 70%;
}
}
</style>
</head>
<body>
<br>
<h2 align="center">泰国站UI自动化回归报告</h2>
<h3>汇总信息</h3>
<table id="summary">
<tr>
<th>开始与结束时间</th>
<td colspan="2">${startTime}</td>
<th>执行时间</th>
<td colspan="2">$DURATION seconds</td>
</tr>
<tr>
<th>运行版本与系统版本</th>
<td colspan="2">${versionName}</td>
<th>设备型号</th>
<td colspan="2">${mobileModel}</td>
</tr>
<tr>
<th>TOTAL</th>
<th>SUCCESS</th>
<th>FAILED</th>
<th>ERROR</th>
<th>SKIPPED</th>
</tr>
<tr>
<td>$TOTAL</td>
<td>$SUCCESS</td>
<td>$FAILED</td>
<td>$ERROR</td>
<td>$SKIPPED</td>
</tr>
</table>
<h3>详情</h3>
#foreach($result in $results.entrySet())
#set($item = $result.value)
<table id="$result.key" class="details">
<tr>
<th>测试类</th>
<td colspan="4">$result.key</td>
</tr>
<tr>
<td>TOTAL: $item.totalSize</td>
<td>SUCCESS: $item.successSize</td>
<td>FAILED: $item.failedSize</td>
<td>ERROR: $item.errorSize</td>
<td>SKIPPED: $item.skippedSize</td>
</tr>
<tr>
<th>Status</th>
<th>Method</th>
<th>Description</th>
<th>Duration</th>
<th>Detail</th>
</tr>
#foreach($testResult in $item.resultList)
<tr>
#if($testResult.status==1)
<th class="success" >success
</td>
#elseif($testResult.status==2)
<th class="failure" >failure
</td>
#elseif($testResult.status==3)
<th class="skipped" >skipped
</td>
#end
<td>$testResult.testName</td>
<td>${testResult.description}</td>
<td>${testResult.duration} seconds</td>
<td class="detail">
<a class="button" href="#popup_log_${testResult.caseName}_${testResult.testName}">log</a>
<div id="popup_log_${testResult.caseName}_${testResult.testName}" class="overlay">
<div class="popup">
<h3>Request and Response data</h3>
<a class="close" href="">×</a>
<div class="content">
<h4>Response:</h4>
<div >
<table>
<tr>
<th>日志</th>
<td>
#foreach($msg in $testResult.twooutparam)
<pre>$msg</pre>
#end
</td>
</tr>
#if($testResult.status==2)
<tr>
<th>异常</th>
<td>
<pre>$testResult.throwableTrace</pre>
</td>
</tr>
#end
</table>
</div>
</div>
</div>
</div>
</td>
</tr>
#end
</table>
#end
<a href="#top">Android前端UI自动化</a>
</body>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。