testng优化:失败重跑,extentReport+appium用例失败截图,测试报告发邮件
生成的单html方便jenkins集成发邮件,= = 构建失败发邮件
参考:https://blog.csdn.net/galen2016/article/details/77975965
步骤:
1.安装Email Extension Plugin插件
2.系统管理--系统设置
--设置jenkins Location的邮箱,这里是管理员邮箱,要和发件人的邮箱设置为同一个
--设置email extension plugin
--设置邮件通知,可以测试一下收发件
2.进入项目-设置-构建后操作
--高级设置
--Default Content内容
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title> </head> <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0"> <div> <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif"> <tr> <th align="center" colspan="2"><br /> <h2>构建信息</h2> </th> </tr> <tr> <td> <ul> <li>项目名称 : ${PROJECT_NAME}</li><br /> <li>详细测试报告 : <a href="${PROJECT_URL}测试报告">${PROJECT_URL}测试报告</a></li><br /> <li>触发原因: ${CAUSE}</li><br /> <li>项目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li><br /> </ul> </td> <td>${JELLY_SCRIPT,template="html"}<br/> </tr> <tr> <td colspan="2" align="center"><br /> <h2>测试报告</h2> </td> </tr> <tr> <td colspan="2" align="center"> <div>${FILE ,path="C:/Users/user/workspace/testngDemo/test-output/emailable-report.html"}</div> </td> </tr> </table> </div> </body> </html>
失败重跑 testng实现 √
https://blog.csdn.net/u011541946/article/details/78688302
有两种实现方法:
1.用runtestng直接java执行
2.用testng的监听
Retry设定重跑次数
public class Retry implements IRetryAnalyzer { private int retryCount =0; private int maxRetryCount =2; @Override public boolean retry(ITestResult result){ if(retryCount<maxRetryCount){ retryCount++; return true; } return false; } }
重跑的监听器
public class RetryTestListener implements IAnnotationTransformer { public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) { IRetryAnalyzer retry = annotation.getRetryAnalyzer(); if (retry == null) { annotation.setRetryAnalyzer(Retry.class); } } }
设置用例结束的监听器
public class ListenerTest extends TestListenerAdapter{ @Override public void onFinish(ITestContext testContext){ super.onFinish(testContext); Iterator<ITestResult> listOfFailedTests=testContext.getFailedTests().getAllResults().iterator(); while(listOfFailedTests.hasNext()){ ITestResult failedTest = listOfFailedTests.next(); ITestNGMethod method = failedTest.getMethod(); if(testContext.getFailedTests().getResults(method).size()>1){ listOfFailedTests.remove(); }else{ if(testContext.getPassedTests().getResults(method).size()>0){ listOfFailedTests.remove(); } } } } }
在testng.xml中配置监听器
<listeners > <listener class-name="b.testng.reportRelated.ListenerTest"/> <listener class-name="com.test.RetryTestListener"/> </listeners>
失败用例截图√
截图方法:
public class ScreenScr { public static void getScreen(TakesScreenshot driver ,String picName){ String filePath=System.getProperty("user.dir"); File pic=driver.getScreenshotAs(OutputType.FILE); // String picName=System.currentTimeMillis()+".png"; try { FileUtils.copyFile(pic, new File(filePath+"\\pic\\"+picName+".png")); } catch (IOException e) { e.printStackTrace(); }finally{ Reporter.log("GetScreenshot Successful"+filePath+"\\pic\\"+picName); } } }
失败和截图结合起来,把截图方法放进TestListenerAdapter的继承类里面。
public class ListenerTest extends TestListenerAdapter{ private Logger logger = Logger.getLogger(ListenerTest.class); private int m_count = 0; String filePath=System.getProperty("user.dir"); @Override public void onTestFailure(ITestResult tr) { super.onTestFailure(tr); String picName =tr.getStartMillis()+""; ScreenScr.getScreen(PhoneDriver.getPhoneDriver(), picName); log("["+tr.getName()+" Failure ]"+"F"); Reporter.log("["+tr.getName()+"失败]"); } @Override public void onTestSkipped(ITestResult tr) { super.onTestSkipped(tr); String picName =tr.getStartMillis()+""; ScreenScr.getScreen(PhoneDriver.getPhoneDriver(), picName); log("["+tr.getName()+" skipped ]"+"S"); Reporter.log("["+tr.getName()+"跳过]"); } }
最后一步,把失败截图放进报告,这里我用的extentReporter,继承了IReporter
这里截取一段用到截图的代码,是extentReporter在报告中,对用例结果的显示的代码,其他项目可以参考。
if (result.getThrowable() != null) { try { String fileName=System.getProperty("user.dir")+"\\pic\\"+result.getStartMillis()+".png"; test.addScreenCaptureFromPath(fileName);//本地可看,klov端裂图,klov的bug //本地和klov都可看 test.fail("截图", MediaEntityBuilder.createScreenCaptureFromPath(fileName).build()); } catch (IOException e) { e.printStackTrace(); } test.log(status, result.getThrowable()); }else { test.log(status, "Test " + status.toString().toLowerCase() + "ed"); }
用例执行失败发测试报告邮件
参考:https://www.cnblogs.com/h--d/p/6138900.html
https://www.cnblogs.com/ysocean/p/7666061.html
步骤
1.写一个发邮件的类Mail,这里把测试报告的html放在附件里面发送了。也可以放在content里面。
package b.testng.reportRelated; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeUtility; public class Email { // supply your SMTP host name private static String host = "smtp.163.com"; // supply a static sendTo list private static String to = "350xxxxx@qq.com"; // supply the sender email private static String from = "yxxxxx@163.com"; private static String password = "Yxxxxxx"; /* * // default cc list 抄送 private static String cc = ""; // default bcc list * 密送 private static String bcc = ""; */ private static String htmlToContents(String htmlPath, String htmlName) { String string = ""; FileReader in; try { in = new FileReader(htmlPath + htmlName); char[] buff = new char[1024 * 10]; in.read(buff); string = new String(buff); in.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return string; } private static void sendEmail(String from, String to, String subject, String content, String htmlPath, String htmlName,String attachPath, String attachName) { try { // 创建参数配置,用于连接邮件服务器 Properties prop = System.getProperties(); prop.setProperty("mail.transport.protocol", "smtp"); prop.setProperty("mail.smtp.host", host); prop.setProperty("mail.smtp.auth", "true"); // 创建会话,和邮件服务器交互 Session session = Session.getDefaultInstance(prop); session.setDebug(true); // 创建一封邮件,设置session,发送者,接收者,标题 MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.setSubject(subject); List<String> toList = getAddress(to); for (String address : toList) { message.addRecipient(Message.RecipientType.TO, new InternetAddress(address)); } String contents=""; if (!htmlPath.equals("")) { contents =htmlToContents(htmlPath, htmlName); } contents=content+contents; if (!attachPath.equals("")) { // 整封邮件的MIME消息体 MimeMultipart msgMultipart = new MimeMultipart("mixed"); // ↑有附件,没有附件会报空指针 message.setContent(msgMultipart); // 往MIME消息体中,加入附件 MimeBodyPart attch = new MimeBodyPart(); msgMultipart.addBodyPart(attch); // 把文件添加到attch中 // 设置数据源 DataSource ds = new FileDataSource(new File(attachPath + attachName)); // 设置数据处理器 DataHandler dHandler = new DataHandler(ds); // 设置第一个附件的数据 attch.setDataHandler(dHandler); // 设置第一个附件的文件名 attch.setFileName(attachName); // 正文 // 往MIME消息体中,加入文字 MimeBodyPart text = new MimeBodyPart(); text.setContent(contents, "text/html;charset=UTF-8"); message.saveChanges(); } else { message.setContent(contents, "text/html;charset=UTF-8"); } // 根据session获取邮件传输对象 Transport transport = session.getTransport(); transport.connect(from, password); transport.sendMessage(message, message.getAllRecipients()); transport.close(); } catch (MessagingException e) { e.printStackTrace(); } } /** * send 2个参数 使用默认邮箱,发送subject和content */ public static void send(String subject, String contents) { sendEmail(from, to, subject, contents, "", "", "", ""); } /** * send 3个参数 发送 指定邮箱,不发到默认邮箱,subject和content */ public static void send(String to, String subject, String contents) { sendEmail(from, to, subject, contents, "", "", "", ""); } /** * sendAttach 4个参数 使用默认邮箱,发送subject和content,attch */ public static void sendAttach(String subject, String contents, String attachPath, String attachName) { sendEmail(from, to, subject, contents, "", "", attachPath, attachName); } /** * sendAttach 3个参数 使用默认邮箱,发送subject和content,attch */ public static void sendAttach(String to, String subject, String contents, String attachPath, String attachName) { sendEmail(from, to, subject, contents, "", "", attachPath, attachName); } private void sendWithPic(String picPath,String picName,String picURL) throws AddressException, MessagingException{ Properties prop = System.getProperties(); prop.setProperty("mail.transport.protocol", "smtp"); prop.setProperty("mail.smtp.host", host); prop.setProperty("mail.smtp.auth", "true"); // 创建会话,和邮件服务器交互 Session session = Session.getDefaultInstance(prop); session.setDebug(true); // 创建一封邮件,设置session,发送者,接收者,标题 MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.setSubject("subject"); List<String> toList = getAddress(to); for (String address : toList) { message.addRecipient(Message.RecipientType.TO, new InternetAddress(address)); } // 5. 创建图片"节点" MimeBodyPart image = new MimeBodyPart(); // 读取本地文件 DataHandler dh = new DataHandler(new FileDataSource(picPath+picName)); // 将图片数据添加到"节点" image.setDataHandler(dh); // 为"节点"设置一个唯一编号(在文本"节点"将引用该ID) image.setContentID(picName); // 6. 创建文本"节点" MimeBodyPart text = new MimeBodyPart(); // 这里添加图片的方式是将整个图片包含到邮件内容中, 实际上也可以以 http 链接的形式添加网络图片 text.setContent("这是一张图片<br/><a href='"+picURL+"'><img src='cid:"+picName+"'/></a>", "text/html;charset=UTF-8"); // 7. (文本+图片)设置 文本 和 图片"节点"的关系(将 文本 和 图片"节点"合成一个混合"节点") MimeMultipart mm_text_image = new MimeMultipart(); mm_text_image.addBodyPart(text); mm_text_image.addBodyPart(image); mm_text_image.setSubType("related"); // 关联关系 // 8. 将 文本+图片 的混合"节点"封装成一个普通"节点" // 最终添加到邮件的 Content 是由多个 BodyPart 组成的 Multipart, 所以我们需要的是 BodyPart, // 上面的 mailTestPic 并非 BodyPart, 所有要把 mm_text_image 封装成一个 BodyPart MimeBodyPart text_image = new MimeBodyPart(); text_image.setContent(mm_text_image); //设置邮件的发送时间,默认立即发送 message.setSentDate(new Date()); } /** * 发送标题,内容,html * * @param subject * @param contents * @param htmlPath * @param htmlName */ public static void sendHtml(String subject, String contents, String htmlPath, String htmlName) { sendEmail(from, to, subject, contents, htmlPath, htmlName, "", ""); } /** * 发送标题,html * * @param subject * @param htmlPath * @param htmlName */ public static void sendHtml(String subject, String htmlPath, String htmlName) { sendEmail(from, to, subject, "", htmlPath, htmlName, "", ""); } private static List<String> getAddress(String address) { List<String> addressList = new ArrayList<String>(); if (address.isEmpty()) return addressList; if (address.indexOf(";") > 0) { String[] addresses = address.split(";"); for (String a : addresses) { addressList.add(a); } } else { addressList.add(address); } return addressList; } }
2.在maven配置中,增加一个插件,该插件可以在测试失败后执行配置的class的main方法
参考https://blog.csdn.net/qq744746842/article/details/51497506
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.1.1</version> <executions> <execution> <phase>test</phase> <goals> <goal>java</goal> </goals> <configuration> <includePluginDependencies>true</includePluginDependencies> <mainClass>b.testng.reportRelated.Email</mainClass> <arguments> <argument>11</argument> <argument>22</argument> </arguments> </configuration> </execution> </executions> </plugin>
package b.testng.reportRelated; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeUtility; public class Email { // supply your SMTP host name private static String host = "smtp.163.com"; // supply a static sendTo list private static String to = "350343***@qq.com"; // supply the sender email private static String from = "yindongzi@163.com"; private static String password = "Yzz1990"; /* * * // default cc list 抄送 private static String cc = ""; // default bcc * list * 密送 private static String bcc = ""; */ private static String htmlToContents(String htmlPath, String htmlName) { String string = ""; FileReader in; try { in = new FileReader(htmlPath + htmlName); char[] buff = new char[1024 * 10]; in.read(buff); string = new String(buff); in.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return string; } private static void sendEmail(String from, String to, String subject, String content, String htmlPath,String htmlName,String attachPath, String attachName) {try {// 创建参数配置,用于连接邮件服务器Properties prop = System.getProperties();prop.setProperty("mail.transport.protocol", "smtp");prop.setProperty("mail.smtp.host", host);prop.setProperty("mail.smtp.auth", "true");// 创建会话,和邮件服务器交互Session session = Session.getDefaultInstance(prop);session.setDebug(true);// 创建一封邮件,设置session,发送者,接收者,标题MimeMessage message = new MimeMessage(session);message.setFrom(new InternetAddress(from));message.setSubject(subject);List<String> toList = getAddress(to);for (String address : toList) {message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));}String contents="";if (!htmlPath.equals("")) {contents =htmlToContents(htmlPath, htmlName);}contents=content+contents;if (!attachPath.equals("")) {// 整封邮件的MIME消息体MimeMultipart msgMultipart = new MimeMultipart("mixed");// ↑有附件,没有附件会报空指针message.setContent(msgMultipart);// 往MIME消息体中,加入附件MimeBodyPart attch = new MimeBodyPart();msgMultipart.addBodyPart(attch);// 把文件添加到attch中// 设置数据源DataSource ds = new FileDataSource(new File(attachPath + attachName));// 设置数据处理器DataHandler dHandler = new DataHandler(ds);// 设置第一个附件的数据attch.setDataHandler(dHandler);// 设置第一个附件的文件名attch.setFileName(attachName);// 正文// 往MIME消息体中,加入文字MimeBodyPart text = new MimeBodyPart();text.setContent(contents, "text/html;charset=UTF-8");message.saveChanges();}else {message.setContent(contents, "text/html;charset=UTF-8");}// 根据session获取邮件传输对象Transport transport = session.getTransport();transport.connect(from, password);transport.sendMessage(message, message.getAllRecipients());transport.close();} catch (MessagingException e) {e.printStackTrace();}} /** * send 2个参数 使用默认邮箱,发送subject和content */ public static void send(String subject, String contents) { sendEmail(from, to, subject, contents, "", "", "", ""); } /** * send 3个参数 发送 指定邮箱,不发到默认邮箱,subject和content */ public static void send(String to, String subject, String contents) { sendEmail(from, to, subject, contents, "", "", "", ""); } /** * sendAttach 4个参数 使用默认邮箱,发送subject和content,attch */ public static void sendAttach(String subject, String contents, String attachPath, String attachName) { sendEmail(from, to, subject, contents, "", "", attachPath, attachName); } /** * sendAttach 3个参数 使用默认邮箱,发送subject和content,attch */ public static void sendAttach(String to, String subject, String contents, String attachPath, String attachName) { sendEmail(from, to, subject, contents, "", "", attachPath, attachName); } private void sendWithPic(String picPath,String picName,String picURL) throws AddressException, MessagingException{Properties prop = System.getProperties();prop.setProperty("mail.transport.protocol", "smtp");prop.setProperty("mail.smtp.host", host);prop.setProperty("mail.smtp.auth", "true");// 创建会话,和邮件服务器交互Session session = Session.getDefaultInstance(prop);session.setDebug(true);// 创建一封邮件,设置session,发送者,接收者,标题MimeMessage message = new MimeMessage(session);message.setFrom(new InternetAddress(from));message.setSubject("subject");List<String> toList = getAddress(to);for (String address : toList) {message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));} // 5. 创建图片"节点" MimeBodyPart image = new MimeBodyPart(); // 读取本地文件 DataHandler dh = new DataHandler(new FileDataSource(picPath+picName)); // 将图片数据添加到"节点" image.setDataHandler(dh); // 为"节点"设置一个唯一编号(在文本"节点"将引用该ID) image.setContentID(picName); // 6. 创建文本"节点" MimeBodyPart text = new MimeBodyPart(); // 这里添加图片的方式是将整个图片包含到邮件内容中, 实际上也可以以 http 链接的形式添加网络图片 text.setContent("这是一张图片<br/><a href='"+picURL+"'><img src='cid:"+picName+"'/></a>", "text/html;charset=UTF-8"); // 7. (文本+图片)设置 文本 和 图片"节点"的关系(将 文本 和 图片"节点"合成一个混合"节点") MimeMultipart mm_text_image = new MimeMultipart(); mm_text_image.addBodyPart(text); mm_text_image.addBodyPart(image); mm_text_image.setSubType("related"); // 关联关系 // 8. 将 文本+图片 的混合"节点"封装成一个普通"节点" // 最终添加到邮件的 Content 是由多个 BodyPart 组成的 Multipart, 所以我们需要的是 BodyPart, // 上面的 mailTestPic 并非 BodyPart, 所有要把 mm_text_image 封装成一个 BodyPart MimeBodyPart text_image = new MimeBodyPart(); text_image.setContent(mm_text_image); //设置邮件的发送时间,默认立即发送 message.setSentDate(new Date());} /** * * 发送标题,内容,html * * @param subject * @param contents * @param htmlPath * * @param htmlName */ public static void sendHtml(String subject, String contents, String htmlPath, String htmlName) { sendEmail(from, to, subject, contents, htmlPath, htmlName, "", ""); } /** * 发送标题,html * * @param subject * @param htmlPath * @param htmlName */ public static void sendHtml(String subject, String htmlPath, String htmlName) { sendEmail(from, to, subject, "", htmlPath, htmlName, "", ""); } private static List<String> getAddress(String address) { List<String> addressList = new ArrayList<String>(); if (address.isEmpty()) return addressList; if (address.indexOf(";") > 0) { String[] addresses = address.split(";"); for (String a : addresses) { addressList.add(a); } } else { addressList.add(address); } return addressList; } }