image.png

引言:
有时工作中有事临时走开,如果执行case过程中没有出错,不出意外是不会看日志了,但是如果忘了看执行结果怎么办??总不能重新运行一次,所以今天讲邮件发送,叮~一声,提醒我们看邮箱。

注:依赖环境Allure、JsonPath、testNG;

1.引入pom依赖:

  1. <!-- https://mvnrepository.com/artifact/javax.mail/mail -->
  2. <dependency>
  3. <groupId>javax.mail</groupId>
  4. <artifactId>mail</artifactId>
  5. <version>1.4.7</version>
  6. </dependency>

2.封装一个发送邮件的util,代码如下:

  1. import com.alibaba.fastjson.JSON;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.jayway.jsonpath.DocumentContext;
  4. import com.jayway.jsonpath.JsonPath;
  5. import org.apache.log4j.Logger;
  6. import javax.activation.DataHandler;
  7. import javax.activation.DataSource;
  8. import javax.activation.FileDataSource;
  9. import javax.mail.*;
  10. import javax.mail.internet.InternetAddress;
  11. import javax.mail.internet.MimeBodyPart;
  12. import javax.mail.internet.MimeMessage;
  13. import javax.mail.internet.MimeMultipart;
  14. import java.io.*;
  15. import java.util.*;
  16. /**
  17. * @author Knife
  18. * @description 发送自动化测试报告
  19. * @createTime 2020-05-13 21:53
  20. */
  21. public class SendEmail extends Rest_Perfect {
  22. private static Logger logger = Logger.getLogger(SendEmail.class);
  23. private static SendEmail sendEmail;
  24. static {
  25. }
  26. /**
  27. * 获取sendEmail对象
  28. *
  29. * @return
  30. */
  31. public static SendEmail getInstance() {
  32. if (Objects.isNull(sendEmail)) {
  33. sendEmail = new SendEmail();
  34. }
  35. return sendEmail;
  36. }
  37. /**
  38. * 发送邮件
  39. *
  40. * @param title 邮件标题
  41. * @param filePath allure-report中suite.json文件路径
  42. * @param addresseeEmail 收件邮箱
  43. */
  44. public static void send(String title, String filePath, String addresseeEmail) {
  45. //根据运行测试类,和main入口判断
  46. String p = SendEmail.class.getResource("/").getPath();
  47. String[] split = p.split("/");
  48. for (String s : split) {
  49. if ("test-classes".equals(s)) {
  50. filePath = p.substring(0, p.indexOf("/test-classes")) + filePath;
  51. }
  52. if ("classes".equals(s)) {
  53. filePath = p.substring(0, p.indexOf("/classes")) + filePath;
  54. }
  55. }
  56. //获取allure报告内容
  57. String sendEmailText = getAllureResult(filePath);
  58. // 创建一个Property文件对象
  59. Properties props = new Properties();
  60. // 设置邮件服务器的信息,这里设置smtp主机名称
  61. props.put("mail.smtp.host", "smtp.163.com");
  62. // 设置socket factory 的端口
  63. props.put("mail.smtp.socketFactory.port", "465");
  64. // 设置socket factory
  65. props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
  66. // 设置需要身份验证
  67. props.put("mail.smtp.auth", "true");
  68. // 设置SMTP的端口,QQ的smtp端口是25
  69. props.put("mail.smtp.port", "25");
  70. // 身份验证实现
  71. Session session = Session.getDefaultInstance(props, new Authenticator() {
  72. protected PasswordAuthentication getPasswordAuthentication() {
  73. // 第二个参数,就是我网易邮箱开启smtp的授权码
  74. return new PasswordAuthentication("Kinfe_1114@163.com", "****这里填写自己邮箱和授权码");
  75. }
  76. });
  77. try {
  78. // 创建一个MimeMessage类的实例对象
  79. Message message = new MimeMessage(session);
  80. // 设置发件人邮箱地址
  81. message.setFrom(new InternetAddress("Kinfe_1114@163.com"));
  82. // 设置收件人邮箱地址
  83. message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(addresseeEmail));
  84. // 设置邮件主题
  85. title = getFormatDateTime() + " " + title;
  86. message.setSubject(title);
  87. // 创建一个MimeBodyPart的对象,以便添加内容
  88. BodyPart messageBodyPart1 = new MimeBodyPart();
  89. // 设置邮件正文内容
  90. messageBodyPart1.setText(sendEmailText);
  91. // 创建另外一个MimeBodyPart对象,以便添加其他内容
  92. MimeBodyPart messageBodyPart2 = new MimeBodyPart();
  93. // 创建一个datasource对象,并传递文件
  94. DataSource source = new FileDataSource(filePath);
  95. // 设置handler
  96. messageBodyPart2.setDataHandler(new DataHandler(source));
  97. // 加载文件
  98. /*messageBodyPart2.setFileName(filePath);*/
  99. // 创建一个MimeMultipart类的实例对象
  100. Multipart multipart = new MimeMultipart();
  101. // 添加正文1内容
  102. multipart.addBodyPart(messageBodyPart1);
  103. // 添加正文2内容
  104. //multipart.addBodyPart(messageBodyPart2);
  105. // 设置内容
  106. message.setContent(multipart);
  107. // 最终发送邮件
  108. Transport.send(message);
  109. logger.info("\n邮件主题:" + title +
  110. "\n邮件内容:" + sendEmailText +
  111. "\n收件人邮箱:" + addresseeEmail +
  112. "\n****************************邮件发送成功****************************");
  113. } catch (MessagingException e) {
  114. logger.error("邮件发送异常!");
  115. logger.error("报错为:" + e);
  116. }
  117. }
  118. /**
  119. * 解析allure-report中suite.json文件内容
  120. *
  121. * @param jsonFilePath
  122. * @return
  123. */
  124. public static String getAllureResult(String jsonFilePath) {
  125. //定义储存用例结果的容器
  126. StringBuffer suiteResult = new StringBuffer();
  127. int p_sum = 0; //执行成功case总数
  128. int f_sum = 0; //执行失败case总数
  129. int timeSum = 0; //运行总共花费时间
  130. //存储测试方法名
  131. Object caseName = null;
  132. try (FileInputStream fileInputStream = new FileInputStream(jsonFilePath)) {
  133. //获取documentContext对象
  134. DocumentContext documentContext = JsonPath.parse(fileInputStream);
  135. //操作documentContext读取json文件中的内容
  136. List<LinkedHashMap<String, Object>> caseList = documentContext.read("$.children[*].children[*].children[*].children[*]");
  137. //遍历获取到的所有测试类
  138. HashMap<String, Object> err_Map = new HashMap<>();
  139. for (LinkedHashMap<String, Object> linkedHashMap : caseList) {
  140. for (Map.Entry<String, Object> entry : linkedHashMap.entrySet()) {
  141. //获取测试方法名
  142. if ("name".equals(entry.getKey())) {
  143. caseName = entry.getValue();
  144. }
  145. //获取case执行结果状态
  146. if ("status".equals(entry.getKey())) {
  147. if ("passed".equals(entry.getValue())) {
  148. p_sum++;
  149. } else if ("failed".equals(entry.getValue())) {
  150. //记录是失败case
  151. err_Map.put("err_Case" + (f_sum + 1), caseName);
  152. f_sum++;
  153. }
  154. }
  155. //获取case执行时间
  156. if ("time".equals(entry.getKey())) {
  157. Map<String, Object> timeMap = JSONObject.parseObject(JSON.toJSONString(entry.getValue()));
  158. Integer duration = (Integer) timeMap.get("duration");
  159. timeSum += duration;
  160. }
  161. }
  162. }
  163. suiteResult.append("\nCase执行总数:" + (p_sum + f_sum));
  164. suiteResult.append("\n通过:" + p_sum + "条");
  165. suiteResult.append("\n失败:" + f_sum + "条");
  166. suiteResult.append((f_sum != 0 ? ("\n执行失败Method:" + err_Map.toString()) : ""));
  167. suiteResult.append("\n执行花费时间:" + (timeSum / 1000) + "秒");
  168. logger.info("allure-report解析成功");
  169. return suiteResult.toString();
  170. } catch (Exception e) {
  171. logger.error("请检查路径,获取json文件内容!");
  172. logger.error("报错内容:" + e);
  173. }
  174. return null;
  175. }
  176. }

3.加一个util执行dos,用于初始化删除allure遗留的json文件,以及将allure.json文件通过serve开启服务,转成html文件直接访问,代码如下:

import org.apache.log4j.Logger;

import java.io.*;


/**
 * @author Knife
 * @description
 * @createTime 2020-05-13 15:49
 */
public class OutputDos extends Rest_Perfect {

    private static Logger logger = Logger.getLogger(OutputDos.class);

    private static String classPath = OutputDos.class.getResource("/").getPath();

    static {
        //获取target根路径
        classPath = classPath.substring(1, classPath.indexOf("test-classes"));
    }


    /**
     * 输入路径(从target根路径输入):删除目录下全部文件
     *
     * @param path
     */
    public static void delAllureResults(String path) {
        File file = new File(classPath + path);
        File[] files = file.listFiles();
        for (File f : files) {
            f.delete();
            logger.info(String.format("当前文件:%s,删除成功", f));
        }
    }

    /**
     * 输入路径(从target根路径输入):results和report得到allure得html报告
     *
     * @param rawPath
     * @param outPath
     */
    public static void allureGenerateHtmlReport(String rawPath, String outPath) {
        StringBuffer dos = new StringBuffer();
        dos.append(String.format("cmd /c allure generate %s -o %s --clean", (classPath + rawPath), (classPath + outPath)));
        //如果还有dos命令需要执行,用"&&"表示下一条
        //dos.append(String.format("&& allure serve %s",(classPath + rawPath)));

        try {
            //执行dos命令
            Process exec = Runtime.getRuntime().exec(dos.toString());
            exec.waitFor();
        } catch (Exception e) {
            logger.error("文件路径错误,请重新输入!");
            logger.error("报错内容:" + e);
        }
    }
}

4.编写一个class取名BaseTest,代码如下:

import cn.Knife.Wework.Utils.OutputDos;
import cn.Knife.Wework.Utils.SendEmail;
import io.qameta.allure.Description;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;

import java.util.Objects;

/**
 * @author Knife
 * @description
 * @createTime 2020-05-13 23:03
 */
public class BaseTest {

    private static OutputDos od;

    private static SendEmail sendEmail = SendEmail.getInstance();

    @Description("删除allure遗留的测试结果")
    @BeforeSuite
    public void be() {
        if (Objects.isNull(od)) {
            od = new OutputDos();
            //运行前删除allure遗留测试结果
            od.delAllureResults("/allure-results");
        }
    }


    @Description("解析allure.json文件为report,发送邮件")
    @AfterSuite
    public void af() {
        //解析allure的json文件为html
        od.allureGenerateHtmlReport("/allure-results", "/allure-report");

        sendEmail.send("接口自动化测试", "/allure-report/data/suites.json", "2538699146@qq.com");

    }
}

开始测试,写一段小代码继承BaseTest,如下:

import cn.Knife.Wework.BaseTest;
import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * @author Knife
 * @description
 * @createTime 2020-05-15 1:59
 */
public class SendEmailTest extends BaseTest {

    @Test
    public void test1() {
        String s = "我来测试了";
        Assert.assertEquals(s,"我来测试了");
    }

    @Test
    public void test2() {
        String s = "我来测试了";
        Assert.assertEquals(s,"我来测试了");
    }
    @Test
    public void test3() {
        String s = "我来测试了";
        Assert.assertEquals(s,"我来测试了");
    }
    @Test
    public void test4() {
        String s = "我来测试了";
        Assert.assertEquals(s,"我来测试了");
    }
}

运行结果:

image.png
image.png
image.png

case执行完,邮件马上发送,下面看一下异常case执行会有什么效果,先增加两条异常用例,如下:

     @Test
    public void errTest() {
        Object o = null;
        Assert.assertNotNull(o);
    }

    @Test
    public void errTestNotEquals(){
        String a = "Knife";
        String b = "Knife";
        Assert.assertNotEquals(a,b);

    }

查看发送的邮件,执行出错的case,会记录下来method,如下:

image.png

结~结束了吗?是的,如果想看完整的接口自动化框架编写,执行过程,请看下面:

语雀内容