自动化测试->测试报告自动发送给指定邮箱

 

 

 

 

引言:

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

 

注:依赖环境Allrue、JsonPath、testNG

 

1.引入pom依赖:

   <!-- https://mvnrepository.com/artifact/javax.mail/mail -->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>

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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import org.apache.log4j.Logger;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.*;
import java.util.*;

/**
 * @author Knife
 * @description 发送自动化测试报告
 * @createTime 2020-05-13 21:53
 */
public class SendEmail extends Rest_Perfect {

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

    private static SendEmail sendEmail;

    static {

    }

    /**
     * 获取sendEmail对象
     *
     * @return
     */
    public static SendEmail getInstance() {
        if (Objects.isNull(sendEmail)) {
            sendEmail = new SendEmail();
        }
        return sendEmail;
    }


    /**
     * 发送邮件
     *
     * @param title          邮件标题
     * @param filePath       allure-report中suite.json文件路径
     * @param addresseeEmail 收件邮箱
     */
    public static void send(String title, String filePath, String addresseeEmail) {

        //根据运行测试类,和main入口判断
        String p = SendEmail.class.getResource("/").getPath();
        String[] split = p.split("/");
        for (String s : split) {
            if ("test-classes".equals(s)) {
                filePath = p.substring(0, p.indexOf("/test-classes")) + filePath;
            }
            if ("classes".equals(s)) {
                filePath = p.substring(0, p.indexOf("/classes")) + filePath;

            }
        }
        //获取allure报告内容
        String sendEmailText = getAllureResult(filePath);

        // 创建一个Property文件对象
        Properties props = new Properties();

        // 设置邮件服务器的信息,这里设置smtp主机名称
        props.put("mail.smtp.host", "smtp.163.com");

        // 设置socket factory 的端口
        props.put("mail.smtp.socketFactory.port", "465");

        // 设置socket factory
        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

        // 设置需要身份验证
        props.put("mail.smtp.auth", "true");

        // 设置SMTP的端口,QQ的smtp端口是25
        props.put("mail.smtp.port", "25");

        // 身份验证实现
        Session session = Session.getDefaultInstance(props, new Authenticator() {

            protected PasswordAuthentication getPasswordAuthentication() {
                // 第二个参数,就是我网易邮箱开启smtp的授权码
                return new PasswordAuthentication("Kinfe_1114@163.com", "****这里填写自己邮箱和授权码");
            }
        });
try {
// 创建一个MimeMessage类的实例对象
Message message = new MimeMessage(session);
// 设置发件人邮箱地址
message.setFrom(new InternetAddress("Kinfe_1114@163.com"));
// 设置收件人邮箱地址
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(addresseeEmail));
// 设置邮件主题
title = getFormatDateTime() + " " + title;
message.setSubject(title);
// 创建一个MimeBodyPart的对象,以便添加内容
BodyPart messageBodyPart1 = new MimeBodyPart();
// 设置邮件正文内容
messageBodyPart1.setText(sendEmailText);
// 创建另外一个MimeBodyPart对象,以便添加其他内容
MimeBodyPart messageBodyPart2 = new MimeBodyPart();
// 创建一个datasource对象,并传递文件
DataSource source = new FileDataSource(filePath);
// 设置handler
messageBodyPart2.setDataHandler(new DataHandler(source));
// 加载文件
/*messageBodyPart2.setFileName(filePath);*/
// 创建一个MimeMultipart类的实例对象
Multipart multipart = new MimeMultipart();
// 添加正文1内容
multipart.addBodyPart(messageBodyPart1);
// 添加正文2内容
//multipart.addBodyPart(messageBodyPart2);
// 设置内容
message.setContent(multipart);
// 最终发送邮件
Transport.send(message);
logger.info("\n邮件主题:" + title +
"\n邮件内容:" + sendEmailText +
"\n收件人邮箱:" + addresseeEmail +
"\n****************************邮件发送成功****************************");
        } catch (MessagingException e) {
logger.error("邮件发送异常!");
logger.error("报错为:" + e);
        }
    }
/**
* 解析allure-report中suite.json文件内容
*
* @param jsonFilePath
* @return
*/
public static String getAllureResult(String jsonFilePath) {
//定义储存用例结果的容器
StringBuffer suiteResult = new StringBuffer();       
int p_sum = 0;  //执行成功case总数
int f_sum = 0;  //执行失败case总数
int timeSum = 0;    //运行总共花费时间
//存储测试方法名
Object caseName = null;
try (FileInputStream fileInputStream = new FileInputStream(jsonFilePath)) {
//获取documentContext对象
DocumentContext documentContext = JsonPath.parse(fileInputStream);
//操作documentContext读取json文件中的内容
List<LinkedHashMap<String, Object>> caseList = documentContext.read("$.children[*].children[*].children[*].children[*]");
//遍历获取到的所有测试类
HashMap<String, Object> err_Map = new HashMap<>();
for (LinkedHashMap<String, Object> linkedHashMap : caseList) {
for (Map.Entry<String, Object> entry : linkedHashMap.entrySet()) {
//获取测试方法名
if ("name".equals(entry.getKey())) {
caseName = entry.getValue();
                    }
//获取case执行结果状态
if ("status".equals(entry.getKey())) {
if ("passed".equals(entry.getValue())) {
p_sum++;
                        } else if ("failed".equals(entry.getValue())) {
//记录是失败case
err_Map.put("err_Case" + (f_sum + 1), caseName);
f_sum++;
                        }
                    }
//获取case执行时间
if ("time".equals(entry.getKey())) {
Map<String, Object> timeMap = JSONObject.parseObject(JSON.toJSONString(entry.getValue()));
Integer duration = (Integer) timeMap.get("duration");
timeSum += duration;
                    }
                }
            }
suiteResult.append("\nCase执行总数:" + (p_sum + f_sum));
suiteResult.append("\n通过:" + p_sum + "条");
suiteResult.append("\n失败:" + f_sum + "条");
suiteResult.append((f_sum != 0 ? ("\n执行失败Method:" + err_Map.toString()) : ""));
suiteResult.append("\n执行花费时间:" + (timeSum / 1000) + "秒");
logger.info("allure-report解析成功");
return suiteResult.toString();
        } catch (Exception e) {
logger.error("请检查路径,获取json文件内容!");
logger.error("报错内容:" + e);
        }
return null;
    }
}

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

 

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

此处为语雀文档,点击链接查看:https://www.yuque.com/kinfe/autotest/gdt65k

 

posted on 2020-05-15 16:42  测开门徒  阅读(94)  评论(1)    收藏  举报

导航