京东质量社区 jmeter 接口自动化测试方案二 (报告优化) 增加 git 路径

taki for 京东 · 2017年03月26日 · 最后由 takaの 回复于 2019年04月30日 · 6122 次阅读

通过 ant 集成 Jmeter 可以做到生成报告,但是有一些弱点我们去优化下,也可以用 xsl 去改抽出来模板,这里面没有用模板,直接代码做的,后面有时间拆出模板

我们先看下正常的 ant+jmeter 教程 [超级链接]http://www.cnblogs.com/puresoul/p/4808416.html

这个方案要结合使用,可以看我的上篇文章 [超级链接]https://testerhome.com/topics/8114


先看下效果图


ant+jmeter 是基于 jmeter 里面的 ant-jmeter-1.1.1.jar 去做的,所以我们可以在这里面加一些自己的东西,其他的执行 JTL 之类的配置和原来的一样参考 [超级链接]http://www.cnblogs.com/puresoul/p/4808416.html

说再多也无用,核心代码如下,这里面依赖几个包如下:jcommon-1.0.16.jar、jfreechart-1.0.13.jar、servlet-api-2.5.jar、jstl-1.2.jar、ant-javamail.jar 欢迎大家参考


  • 1.创建一个可以供 ant 执行的类,我们也需要和原来的配置一样加一些东西进去,这里加了 ReportTask,下面所有的类都可以直接扔到 ant-jmeter-1.1.1.jar 里
<target name="reportTask">
        <taskdef name="myReportTask" classname="org.programmerplanet.ant.taskdefs.jmeter.ReportTask" />
            <myReportTask in="${jmeter.result.jtlName}"
                out="${jmeter.result.htmlName}"
                mailhost="smtp.qq.com"
                ssl="true"
                user="qq号"
                password="授权密码"
                mailTitle="接口测试报告"
                from="qq@qq.com"
                toAddress="qq@qq.com"
                runUser="写执行人的名字">
            </myReportTask>
    </target>

因为是通过 ant 命令行执行的,所以日志信息我用 System.out 输出的,ReportTask 里面的属性和 xml 里面一致

package org.programmerplanet.ant.taskdefs.jmeter;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

import java.io.File;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: jiaou
 * Date: 16-11-7
 * Time: 下午12:52
 * To change this template use File | Settings | File Templates.
 */
public class ReportTask extends Task {
    private String in;
    private String out;
    private String mailhost;
    private String mailport;
    private boolean ssl;
    private String user;
    private String password;
    private String mailTitle;
    private String from;
    private String toAddress;
    private String runUser;

    /**
     * @see Task#execute()
     */
    public void execute() throws BuildException {
        System.out.println("开始执行收集报告任务");
        if (null == in || in.equals("")) {
            System.out.println("in,Jtl文件不能为空!");
            return;
        }
        if (null == out || out.equals("")) {
            System.out.println("out,输出目录不能为空!");
            return;
        }
        File directory = new File(out);
        String outputPath = "";
        try {
            outputPath = directory.getParent();
        } catch (Exception e) {
            System.out.println("获取输出文件路径异常,请确认输入的是正确的文件路径");
            e.printStackTrace();
            return;
        }
        String mailContent = "";
        try {
            //读取JTL文件获取所用的用例
            List<ReportCase> reportCaseList = Report.getReportList(in);
            //把所有用例写入html文件
            Report.writeReport(reportCaseList,0,out,runUser);
            String pngPath = outputPath + "\\report.png";
            //生成通过率撸片
            Report.writePng(pngPath);
            //获取邮件正文
            mailContent = MailReport.reportHtml(Report.allCase, Report.passCase, Report.runTime, runUser);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        System.out.println("报告和图片生成完成,开始发送邮件");
        //smtp.qq.com
        MailObj mailObj = new MailObj();
        mailObj.setHost(mailhost);
        mailObj.setPort(mailport);
        mailObj.setMailTitle(mailTitle);
        mailObj.setName(user);
        mailObj.setPassword(password);
        mailObj.setFrom(from);
        mailObj.setToAddress(toAddress);
        mailObj.setSsl(ssl);
        mailObj.setMailContent(mailContent);
        mailObj.setReportPath(outputPath);
        mailObj.setHtmlPath(out);
        try {
            SendEmail.sendEmail(mailObj);
        } catch (Exception e) {
            System.out.println("发送邮件失败:" + e);
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

    }

    public void setIn(String in) {
        this.in = in;
    }

    public void setOut(String out) {
        this.out = out;
    }

    public void setMailhost(String mailhost) {
        this.mailhost = mailhost;
    }

    public void setMailport(String mailport) {
        this.mailport = mailport;
    }

    public void setSsl(boolean ssl) {
        this.ssl = ssl;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setMailTitle(String mailTitle) {
        this.mailTitle = mailTitle;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public void setToAddress(String toAddress) {
        this.toAddress = toAddress;
    }

    public String getIn() {
        return in;
    }

    public String getOut() {
        return out;
    }

    public String getMailhost() {
        return mailhost;
    }

    public String getMailport() {
        return mailport;
    }

    public boolean isSsl() {
        return ssl;
    }

    public String getUser() {
        return user;
    }

    public String getPassword() {
        return password;
    }

    public String getMailTitle() {
        return mailTitle;
    }

    public String getFrom() {
        return from;
    }

    public String getToAddress() {
        return toAddress;
    }

    public String getRunUser() {
        return runUser;
    }

    public void setRunUser(String runUser) {
        this.runUser = runUser;
    }
}


  • 2.接下来是用于读取 JTL 文件生成 HTML 文件的类,我都加了注释了
package org.programmerplanet.ant.taskdefs.jmeter;


import org.jfree.data.general.DefaultPieDataset;
import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: jiaou
 * Date: 16-7-23
 * Time: 上午10:51
 * To change this template use File | Settings | File Templates.
 */
public class Report {
    public static int allCase = 0;
    public static int passCase = 0;
    public static int caseNum = 0;
    public static long runTime = 0;
    private static NumberFormat numberFormat = NumberFormat.getInstance();

    /*
    * @Description:读取JTL生成报告文件,转换成List
    * @param:jtl文件路径
    * @return:list对象
    * */
    public static List<ReportCase> getReportList(String jtlPath) throws Exception {
        System.out.println("开始读取JTL文件,路径:" + jtlPath);
        Document document = null;
        DocumentBuilder documentBuilder = null;
        File file = new File(jtlPath);
        try {
            DocumentBuilderFactory factory = null;
            factory = DocumentBuilderFactory.newInstance();
            documentBuilder = factory.newDocumentBuilder();
            document = documentBuilder.parse(file);
        } catch (Exception e) {
            System.out.println("转换JTL文件失败,请确认JTL格式是否正确:" + e);
            throw new Exception(e);
        }
        System.out.println("读取JTL文件完毕,开始获取测试用例:");
        Element element = document.getDocumentElement();
        NodeList nodeList = element.getChildNodes();
        List<ReportCase> reportCaseList = new ArrayList<ReportCase>();
        long startTime = 0;
        long endTime = 0;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            String nodeName = node.getNodeName();
            if (nodeName.contains("Sample")) {
                ReportCase reportCase = new ReportCase();
                NamedNodeMap namedNodeMap = node.getAttributes();
                try {
                    //获取测试用例名称
                    Node lb = namedNodeMap.getNamedItem("lb");
                    String caseName = lb.getNodeValue();
                    reportCase.setCaseName(caseName);

                    //获取运行时间
                    Node runTimeNode = namedNodeMap.getNamedItem("t");
                    if (!isEmpty(runTimeNode)) {
                        reportCase.setRunTime(runTimeNode.getNodeValue() + "ms");
                    }
                    //获取code码
                    Node responseCodeNode = namedNodeMap.getNamedItem("rc");
                    if (!isEmpty(responseCodeNode)) {
                        reportCase.setResponseCode(responseCodeNode.getNodeValue());
                    }
                    //获取responseMessage
                    Node responseMessageNode = namedNodeMap.getNamedItem("rm");
                    if (!isEmpty(responseMessageNode)) {
                        reportCase.setResponseMessage(responseMessageNode.getNodeValue());
                    }
                    //获取开始时间
                    Node startTimeNode = namedNodeMap.getNamedItem("ts");
                    if (!isEmpty(startTimeNode)) {
                        String startTimeStr = startTimeNode.getNodeValue();
                        if (allCase == 0) {
                            startTime = Long.parseLong(startTimeStr);
                        }
                        if (i == nodeList.getLength() - 1) {
                            endTime = Long.parseLong(startTimeStr);
                        }
                    }
                    //获取是否执行成功
                    Node statusNode = namedNodeMap.getNamedItem("s");
                    if (!isEmpty(statusNode)) {
                        String s = statusNode.getNodeValue();
                        boolean status = Boolean.parseBoolean(s);
                        reportCase.setStatus(status);
                    }
                    //获取断言对象
                    Node assertionNode = getNodeByName(node, "assertionResult");
                    if (!isEmpty(assertionNode)) {
                        AssertionResult assertionResult = formatAssertion(assertionNode);
                        reportCase.setAssertionResult(assertionResult);
                    } else {
                        reportCase.setAssertionResult(null);
                    }
                    //获取响应数据
                    Node responseDataNode = getNodeByName(node, "responseData");
                    if (!isEmpty(responseDataNode)) {
                        reportCase.setResponseData(responseDataNode.getTextContent());
                    }
                    //获取方法名称
                    Node methodNode = getNodeByName(node, "method");
                    if (!isEmpty(methodNode)) {
                        reportCase.setMethodName(methodNode.getTextContent());
                    }
                    //获取入参
                    Node queryStringNode = getNodeByName(node, "queryString");
                    if (!isEmpty(queryStringNode)) {
                        reportCase.setQueryString(queryStringNode.getTextContent());
                    }
                    //获取接口名
                    Node urlNode = getNodeByName(node, "java.net.URL");
                    if (!isEmpty(urlNode)) {
                        reportCase.setUrl(urlNode.getTextContent());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                allCase++;
                reportCaseList.add(reportCase);
            }
        }
        if (endTime - startTime > 1000) {
            runTime = (endTime - startTime) / 1000;
        } else {
            runTime = 1;
        }
        System.out.println("用例转换完毕");
        return reportCaseList;
    }
    /*
       * @Description:判断是否为空
       * @param:Object o
       * @return:boolean
    * */
    private static boolean isEmpty(Object o) {
        boolean f = false;
        if (o == null) {
            f = true;
        }
        return f;
    }

    /*
       * @Description:将断言结果转换成对象
       * @param:node对象
       * @return:断言对象
       * */
    private static AssertionResult formatAssertion(Node assertionNode) {
        AssertionResult assertionResult = new AssertionResult();
        Node nameNode = getNodeByName(assertionNode, "name");
        assertionResult.setName(nameNode.getTextContent());
        Node failureNode = getNodeByName(assertionNode, "failure");
        boolean failure = Boolean.parseBoolean(failureNode.getTextContent());
        assertionResult.setFailure(failure);
        Node errorNode = getNodeByName(assertionNode, "error");
        boolean error = Boolean.parseBoolean(errorNode.getTextContent());
        assertionResult.setError(error);
        Node failureMessageNode = getNodeByName(assertionNode, "failureMessage");
        if (!isEmpty(failureMessageNode)) {
            assertionResult.setFailureMessage(failureMessageNode.getTextContent());
        }
        return assertionResult;
    }

    /*
     * @Description:通过名字获取节点
     * @param:node,name
     * @return:Node
    * */
    public static Node getNodeByName(Node node, String name) {
        NodeList msgNodeList = node.getChildNodes();
        Node node1 = null;
        for (int k = 0; k < msgNodeList.getLength(); k++) {
            Node childNode = msgNodeList.item(k);
            String childName = childNode.getNodeName();
            if (childName.equalsIgnoreCase(name)) {
                node1 = childNode;
                break;
            }
        }
        return node1;
    }

    /*
     * @Description:输出图片
     * @param:输出图片路径
     * @return:
     * */
    public static void writePng(String pngPath) {
        System.out.println("开始生成饼状图!!!");
        try {
            DefaultPieDataset dataSet = new DefaultPieDataset();
            String casePassCount = passCase + "";
            String caseFailCount = (allCase - passCase) + "";
            dataSet.setValue("PassCase", new Double(casePassCount));
            dataSet.setValue("FailCase", new Double(caseFailCount));
            PicReport picReport = new PicReport();
            picReport.save(dataSet, pngPath, "通过率");
            System.out.println("饼状图生成结束,路径:" + pngPath);
        } catch (Exception e) {
            System.out.println("饼状图生成失败:" + e);
        }
    }
    /*
      * @Description:生成html报告文件
      * @param:测试用例集合,运行时间,输出路径,执行人
      * @return:
    * */
    public static void writeReport(List<ReportCase> reportCaseList, long runTime, String htmlPath, String runUser) {
        String title = getTitle() + getBodyTitle();
        String suiteBody = getSuiteList(reportCaseList) + getCaseStep(reportCaseList) + htmlEnd();
        String summary = getSummary(runTime, runUser);
        String html = title + summary + suiteBody;
        try {
            File htmlPathFile = new File(htmlPath);
            String pathParent = htmlPathFile.getParent();
            File parentFile = new File(pathParent);
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        FileUtil.writeFile(htmlPath, html);
        System.out.println("写入Html文件结束!!!");
    }

    private static String getTitle() {
        return getHeadStart() + ReportStyle.getStyle() + ReportJavaScript.getJavaScript() + getHeadEnd();
    }
    /*
    * @Description:生成html报告文件
    * @param:测试用例集合,运行时间,输出路径,执行人
    * @return:
    * */
    private static String getBodyTitle() {
        String reportName = "接口自动化测试报告";
        String date = DateUtil.getCurrentDate();
        String bodyTitle = "<body><h1>" + reportName + "</h1>" +
                "<table width=\"100%\">" +
                "    <tr>" +
                "        <td align=\"left\">Date report: " + date + "</td>" +
                "        <td align=\"right\">Designed for use with <a href=\"#\">longteng</a> and <a href=\"#\">贾鸥</a>.</td>" +
                "    </tr>" +
                "</table>";
        return bodyTitle;
    }

    /*
   * @Description:获取概要的内容
   * @param:运行时间,执行人
   * @return:
   * */
    private static String getSummary(long runTimeLong, String runUser) {
        int failCase = allCase - passCase;
        String passRate = numberFormat.format((float) passCase / (float) allCase * 100) + "%";  //通过率
        String runTime = runTimeLong + "秒";
        String ip = OS.getLocalIP();
        String summary = "" +
                "<hr size=\"1\">" +
                "<h2>概要</h2>" +
                "<table align=\"center\" class=\"summary\" >" +
                "    <tr valign=\"top\">" +
                "        <th>用例总数</th>" +
                "        <th>通过数</th>" +
                "        <th>失败数</th>" +
                "        <th>通过率</th>" +
                "        <th>运行时间</th>" +
                "        <th>执行机器IP</th>" +
                "        <th>执行人</th>" +
                "    </tr>" +
                "    <tr valign=\"top\" class=\"\">" +
                "        <td align=\"center\">" + allCase + "</td>" +
                "        <td align=\"center\">" +
                "           <span style=\"color: green\">" +
                "             <b>" + passCase + "</b>" +
                "           </span>" +
                "        </td>" +
                "        <td align=\"center\">" +
                "              <span style=\"color: red\">" +
                "             <b>" + failCase + "</b>" +
                "           </span></td>" +
                "        <td align=\"center\">" + passRate + "</td>" +
                "        <td align=\"center\">" + runTime + "</td>" +
                "        <td align=\"center\">" + ip + "</td>" +
                "        <td align=\"center\">" + runUser + "</td>" +
                "    </tr>" +
                "</table>" +
                "</br>";
        return summary;
    }
    /*
      * @Description:获取左侧菜单用例集
      * @param:reportCaseList
      * @return:str
      * */
    private static String getSuiteList(List<ReportCase> reportCaseList) {
        String menuStr = "<hr size=\"1\" width=\"100%\" align=\"center\">" +
                "<div>" +
                "    <div id=\"div_left\" style=\"overflow:auto\">" +
                " <table id=\"suites\">";
        String suites = "";
        int i = 0;
        String suiteName = "用例列表";
        String tbodyId = "tests-" + i;
        String toggleId = "toggle-" + i;
        suites += "<thead>" +
                "            <tr>" +
                "                <th class=\"header suite\" onclick=\"toggleElement('" + tbodyId + "', 'table-row-group'); toggle('" + toggleId + "')\">" +
                "                    <span id=\"" + toggleId + "\" class=\"toggle\">&#x25bc;</span>" +
                "                    <span id=\"" + i + "\">" + suiteName + "</span>" +
                "                </th>" +
                "            </tr>" +
                "            </thead>";
        String suiteCase = getCaseList(tbodyId, reportCaseList);
        suites += suiteCase;
        String center = "<div id=\"div_center\">" +
                "    </div>";
        menuStr = menuStr + suites + "  </table></div>" + center;
        return menuStr;
    }
    /*
      * @Description:获取左侧菜单用例集
      * @param:reportCaseList
      * @return:str
      * */
    private static String getCaseList(String tbodyId, List<ReportCase> caseList) {
        String tBody = "<tbody id=\"" + tbodyId + "\" class=\"tests\">";
        String div = "<div id=\"allSpan\" style=\"display:none\">";
        for (int i = 0; i < caseList.size(); i++) {
            ReportCase reportCase = caseList.get(i);
            String caseName = reportCase.getCaseName();
            boolean caseStatus = reportCase.isStatus();
            tBody += "<tr><td class=\"test\">";
            allCase++;
            div += "<div>";
            if (caseStatus) {
                passCase++;
                tBody += " <span class=\"successIndicator\" title=\"全部通过\">&#x2714;</span>";
                div += " <span class=\"successIndicator\" title=\"全部通过\">&#x2714;</span>";
            } else {
                tBody += " <span class=\"failureIndicator\" title=\"部分失败\">&#x2718;</span>";
                div += " <span class=\"failureIndicator\" title=\"部分失败\">&#x2718;</span>";
            }
            tBody += "   <a id=\"Case" + caseNum + "\" href=\"#\" onclick=\"showDetail(this)\">" + caseName + "</a>" +
                    "   </td>" +
                    "   </tr>";
            div += "<a  href=\"#\" onclick=\"showDetail(this)\">" + caseName + "</a>";
            div += "</div>";
            caseNum++;
        }
        div += "</div>";
        tBody += div;
        tBody += "</tbody>";
        return tBody;
    }

/*
      * @Description:获取报告正文
      * @param:reportCaseList
      * @return:str
      * */
    private static String getCaseStep(List<ReportCase> reportCaseList) {
        String div = "<div id=\"div_right\" style=\"overflow:auto\">";
        div += "<ol id=\"right-panel\">\n";
        caseNum = 0;
        String caseDiv = "";
        String firstCaseName = "";
        String step = "";
        for (int k = 0; k < reportCaseList.size(); k++) {
            ReportCase reportCase = reportCaseList.get(k);
            String caseName = reportCase.getCaseName();
            String display = "none";
            if (caseNum == 0) {
                display = "";
                firstCaseName = caseName;
            }
            step += "<div id =\"parentCase" + caseNum + "\" style=\"display: " + display + "\">";
            step += caseDetail(reportCase);
            step += "</div>";
            caseNum++;
        }
        caseDiv = caseDiv + step;
        div += "<table>" +
                "<tr>" +
                "<td><h1 id =\"caseName\">当前用例:" + firstCaseName + "</h1></td>" +
                "<td>\n" +
                "    &nbsp;&nbsp;&nbsp;&nbsp;\n" +
                "</td>\n" +
                "<td>\n" +
                "    <h1>用例状态类型:</h1>\n" +
                "</td>\n" +
                "<td>\n" +
                "    <select onchange=\"showCaseType(this)\">\n" +
                "        <option value=\"all\" selected=\"selected\">全部用例</option>\n" +
                "        <option value=\"successIndicator\">通过用例</option>\n" +
                "        <option value=\"failureIndicator\">失败用例</option>\n" +
                "    </select>\n" +
                "</td>\n" +
                "<td>\n" +
                "    &nbsp;&nbsp;&nbsp;&nbsp;\n" +
                "</td>\n" +
                "<td>\n" +
                "    <input id=\"searchText\" type=\"text\">\n" +
                "    <button onclick=\"searchCase()\">搜索用例</button>\n" +
                "</td></tr>" +
                "</table>";
        div += caseDiv;
        div += "<input id=\"allCaseNum\" type=\"hidden\" value=\"" + caseNum + "\">" +
                " <input id=\"currentCaseId\" type=\"hidden\" value=\"parentCase0\">" +
                "</div></div></ol>";
        return div;
    }
    /*
          * @Description:获取用例详细信息
          * @param:reportCaseList
          * @return:str
    * */
    private static String caseDetail(ReportCase reportCase) {
        AssertionResult assertionResult = reportCase.getAssertionResult();
        String div = "<div class=\"group\">Sampler</div>\n" +
                "<div class=\"zebra\">\n" +
                "<table>\n" +
                "<tr><td class=\"data key\">Time</td><td class=\"data delimiter\">:</td><td class=\"data\">" + reportCase.getRunTime() + "</td></tr>\n" +
                "<tr><td class=\"data key\">Response Code</td><td class=\"data delimiter\">:</td><td class=\"data\">" + reportCase.getResponseCode() + "</td></tr>\n" +
                "<tr><td class=\"data key\">Response Message</td><td class=\"data delimiter\">:</td><td class=\"data\">" + reportCase.getResponseMessage() + "</td></tr>\n" +
                "</table>\n" +
                "</div>\n";
        if (null != assertionResult) {
            if (assertionResult.isFailure() || assertionResult.isError()) {
                div += "<div class=\"trail\"></div>\n" +
                        "<div class=\"group\">Assertion</div>\n" +
                        "<div class=\"zebra\">\n" +
                        "<table>\n" +
                        "<tbody class=\"failure\"><tr><td class=\"data assertion\" colspan=\"3\">" + assertionResult.getName() + "</td></tr>\n" +
                        "<tr><td class=\"data key\">Failure</td><td class=\"data delimiter\">:</td><td class=\"data\">" + assertionResult.isFailure() + "</td></tr>\n" +
                        "<tr><td class=\"data key\">Error</td><td class=\"data delimiter\">:</td><td class=\"data\">" + assertionResult.isError() + "</td></tr>\n" +
                        "<tr><td class=\"data key\">Failure Message</td><td class=\"data delimiter\">:</td>\n" +
                        "<td class=\"data\">" + assertionResult.getFailureMessage() + "</td></tr></tbody></table></div>\n";
                ;
            }
        }
        div += "<div class=\"trail\">\n" +
                "</div><div class=\"group\">Request</div>\n" +
                "<div class=\"zebra\">" +
                "<table>" +
                "<tr><td class=\"data key\">接口/Url</td><td class=\"data delimiter\">:</td><td class=\"data\"><pre class=\"data\">" + reportCase.getUrl() + "</pre></td></tr>" +
                "<tr><td class=\"data key\">Method</td><td class=\"data delimiter\">:</td><td class=\"data\"><pre class=\"data\">" + reportCase.getMethodName() + "</pre></td></tr>" +
                "<tr><td class=\"data key\">Query String</td><td class=\"data delimiter\">:</td><td class=\"data\"><pre class=\"data\">" + reportCase.getQueryString() + "</pre></td></tr>\n" +
                "</table>\n" +
                "</div>\n" +
                "<div class=\"trail\"></div>\n" +
                "<div class=\"group\">Response</div><div class=\"zebra\">\n" +
                "<table>\n" +
                "<tr><td class=\"data key\">Response Data</td><td class=\"data delimiter\">:</td><td class=\"data\">\n" +
                "<pre class=\"data\">" + reportCase.getResponseData() + "</pre></td></tr>\n" +
                "</table>\n" +
                "</div>\n";
        return div;
    }


    private static String getHeadStart() {
        String title = "<!DOCTYPE html private \"-//W3C//DTD HTML 4.01 Transitional//EN\">" +
                "<html>" +
                "<head>" +
                "    <META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">" +
                "    <title>Load Test Results</title>";

        return title;
    }
    private static String htmlEnd() {
        return "</div></div></body></html>";
    }
    private static String getHeadEnd() {
        return "</head>";
    }

}


  • 3.生成饼状图的类
package org.programmerplanet.ant.taskdefs.jmeter;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.servlet.ServletUtilities;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.general.DefaultPieDataset;


import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * Created with IntelliJ IDEA.
 * User: jiaou
 * Date: 14-5-5
 * Time: 下午11:05
 * To change this template use File | Settings | File Templates.
 */
public class PicReport extends ServletUtilities {
    /**
     * 饼状图
     */
    public static void pieChart3D(DefaultPieDataset dataset, String fileName, String titleName) {
        JFreeChart chart = ChartFactory.createPieChart3D(titleName, dataset,
                true, true, false);
        PiePlot3D plot = (PiePlot3D) chart.getPlot();
        plot.setSectionPaint("FailCase", Color.RED);
        plot.setSectionPaint("PassCase", Color.green);

        // 图片中显示百分比:默认方式
        // plot.setLabelGenerator(new
        // StandardPieSectionLabelGenerator(StandardPieToolTipGenerator.DEFAULT_TOOLTIP_FORMAT));
        // 图片中显示百分比:自定义方式,{0} 表示选项, {1} 表示数值, {2} 表示所占比例 ,小数点后两位
        plot.setLabelGenerator(new StandardPieSectionLabelGenerator(
                "{0}({2})", NumberFormat.getNumberInstance(),
                new DecimalFormat("0.00%")));
        // 图例显示百分比:自定义方式, {0} 表示选项, {1} 表示数值, {2} 表示所占比例
        plot.setLegendLabelGenerator(new StandardPieSectionLabelGenerator(
                "{0}={1}({2})"));
        // 设置背景色为白色
        chart.setBackgroundPaint(Color.white);
        // 指定图片的透明度(0.0-1.0)
        plot.setForegroundAlpha(1.0f);
        // 指定显示的饼图上圆形(false)还椭圆形(true)
        plot.setCircular(true);
        // 设置图标题的字体
        Font font = new Font(" 黑体", Font.CENTER_BASELINE, 20);
        TextTitle title = new TextTitle(titleName);
        title.setFont(font);
        chart.setTitle(title);
        plot.setLabelFont(new Font("SimSun", 0, 15));//
        LegendTitle legend = chart.getLegend(0);
        legend.setItemFont(new Font("宋体", Font.BOLD, 16));
        try {
            ChartUtilities.saveChartAsJPEG(
                    new File(fileName), //输出到哪个输出流
                    1, //JPEG图片的质量,0~1之间
                    chart, //统计图标对象
                    640, //宽
                    300,//宽
                    null //ChartRenderingInfo 信息
            );
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    public void save(DefaultPieDataset dataset, String fileName, String titleName) {
        pieChart3D(dataset, fileName, titleName);
    }
}


  • 4.邮件正文
package org.programmerplanet.ant.taskdefs.jmeter;


import java.text.NumberFormat;

/**
 * Created with IntelliJ IDEA.
 * User: jiaou
 * Date: 16-7-23
 * Time: 下午10:51
 * To change this template use File | Settings | File Templates.
 */
public class MailReport {
    private static NumberFormat numberFormat = NumberFormat.getInstance();
    /**
     * 获取发送邮件的征文
     * @return String
     */
    public static String reportHtml(int allCase,int passCase,long runTime,String user) {
        String ip = OS.getLocalIP();
        int failCase = allCase - passCase;
        String passRate = numberFormat.format((float) passCase / (float) allCase * 100) + "%";  //通过率
        String h = "<TABLE Align='center' width=1000px>\n" +
                "    <TR>\n" +
                "        <TD Align='center'>\n" +
                "         <span style='font-family:微软雅黑;font-size:42px;font-weight:normal;font-style:italic;text-decoration:none;color:#31c5ff;'><strong>自动化测试报告</strong></span>\n" +
                "        </TD>\n" +
                "    </TR>\n" +
                "</table>\n" +
                "<div>\n" +
                "    <hr size=\"1\" width=\"90%\">\n" +
                "    <TABLE Align='center' class=\"details\"  border=0 cellpadding=5 cellspacing=2 width=85%>\n" +
                "        <tr>\n" +
                "            <td>\n" +
                "            <h1>概要</h1>\n" +
                "            </td>\n" +
                "            <Td colspan=\"6\"></Td>\n" +
                "        </tr>\n" +
                "        <tr valign=\"top\">\n" +
                "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                "                用例总数\n" +
                "            </th>\n" +
                "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                "                通过数\n" +
                "            </th>\n" +
                "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                "                失败数\n" +
                "            </th>\n" +
                "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                "                通过率\n" +
                "            </th>\n" +
                "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                "                运行时间\n" +
                "            </th>\n" +
                "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                "                执行机器IP\n" +
                "            </th>\n" +
                "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                "                执行人\n" +
                "            </th>\n" +
                "        </tr>\n" +
                "        <tr valign=\"top\" class=\"Failure\">\n" +
                "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">" + allCase + "</td>\n" +
                "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\"><span style =\"color: green;\">" + passCase + "</span></td>\n" +
                "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\"><span style =\"color: red;\">" + failCase + "</span></td>\n" +
                "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">" + passRate + "</td>\n" +
                "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">" + runTime + "秒</td>\n" +
                "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">" + ip + "</td>\n" +
                "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">" + user + "</td>\n" +
                "        </tr>\n" +
                "\n" +
                "    </table>\n";
        String detail =
                "    <TABLE Align='center' class=\"details\"  border=0 cellpadding=5 cellspacing=2 width=80%>\n" +
                        "        <tr>\n" +
                        "            <td>\n" +
                        "                <h1>详细</h1>\n" +
                        "            </td>\n" +
                        "            <Td colspan=\"6\"></Td>\n" +
                        "        </tr>\n" +
                        "        <tr valign=\"top\">\n" +
                        "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                        "                测试场景\n" +
                        "            </th>\n" +
                        "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                        "                用例数量\n" +
                        "            </th>\n" +
                        "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                        "                通过用例\n" +
                        "            </th>\n" +
                        "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                        "                失败用例\n" +
                        "            </th>\n" +
                        "            <th style=\" color: #ffffff;font-weight: bold;text-align: center;background: #2674a6;white-space: nowrap;\">\n" +
                        "                通过率\n" +
                        "            </th>\n" +
                        "        </tr>\n" +
                        "        <tr valign=\"top\" class=\"Failure\">\n" +
                        "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">22</td>\n" +
                        "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">7</td>\n" +
                        "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">68.18%</td>\n" +
                        "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">25 ms</td>\n" +
                        "            <td align=\"center\" style=\"background: #eeeee0;white-space: nowrap;\">NaN</td>\n" +
                        "        </tr>\n" +
                        "    </table>\n" +
                        "</div></br>";
        h = h + "<TABLE Align='center'  width=600px  >\n" +
                "<tr> <td>" +
                "<h1></h1></td></tr>" +
                "    <tr>" +
                "        <td Align='center'>\n" +
                "            <img src='cid:passRate'' width=800 height=400  Align='center' alt=''>\n" +
                "        </td>\n" +
                "    </tr>\n" +
                "</table>\n";
        return h;
    }
}



  • 5.css 样式的类,样式我没有抽取出来直接放在代码里面了,弄模板还没有直接调来的快
package org.programmerplanet.ant.taskdefs.jmeter;

/**
 * Created by jiaou on 2016/12/26.
 */
public class ReportStyle {

    public static String getStyle() {
        String style = "<style type=\"text/css\">\n" +
                "        /*概body的基本样式*/\n" +
                "        body {\n" +
                "            font: normal 68% verdana, arial, helvetica;\n" +
                "            color: #000000;\n" +
                "        }\n" +
                "\n" +
                "        /*概要表格的样式开始*/\n" +
                "        table tr td, table tr th {\n" +
                "            font-size: 68%;\n" +
                "        }\n" +
                "\n" +
                "        table.summary {\n" +
                "            border: 0;\n" +
                "            cellpadding: 5;\n" +
                "            cellspacing: 2;\n" +
                "            width: 95%;\n" +
                "        }\n" +
                "\n" +
                "        table.summary tr th {\n" +
                "            color: #ffffff;\n" +
                "            font-weight: bold;\n" +
                "            text-align: center;\n" +
                "            background: #2674a6;\n" +
                "            white-space: nowrap;\n" +
                "        }\n" +
                "\n" +
                "        table.summary tr td {\n" +
                "            background: #eeeee0;\n" +
                "            white-space: nowrap;\n" +
                "        }\n" +
                "\n" +
                "        /*概要表格的样式结束*/\n" +
                "\n" +
                "        /*标题样式结束*/\n" +
                "        h1 {\n" +
                "            margin: 0px 0px 5px;\n" +
                "            font: 165% verdana, arial, helvetica\n" +
                "        }\n" +
                "\n" +
                "        h2 {\n" +
                "            margin-top: 1em;\n" +
                "            margin-bottom: 0.5em;\n" +
                "            font: bold 125% verdana, arial, helvetica\n" +
                "        }\n" +
                "\n" +
                "        /*标题样式结束*/\n" +
                "\n" +
                "        /*左侧用例列表的样式*/\n" +
                "        #div_left {\n" +
                "            float: left;\n" +
                "            width: 15%;\n" +
                "            height: 100%;\n" +
                "        }\n" +
                "\n" +
                "        .suite {\n" +
                "            background-color: #999999;\n" +
                "            font-weight: bold;\n" +
                "        }\n" +
                "\n" +
                "        #suites {\n" +
                "            line-height: 1.7em;\n" +
                "            border-spacing: 0.1em;\n" +
                "            width: 100%;\n" +
                "        }\n" +
                "\n" +
                "        .header {\n" +
                "            font-size: 1.0em;\n" +
                "            font-weight: bold;\n" +
                "            text-align: left;\n" +
                "        }\n" +
                "\n" +
                "        .header.suite {\n" +
                "            cursor: pointer;\n" +
                "            clear: right;\n" +
                "            height: 1.214em;\n" +
                "            margin-top: 1px;\n" +
                "        }\n" +
                "\n" +
                "        .toggle {\n" +
                "            font-family: monospace;\n" +
                "            font-weight: bold;\n" +
                "            padding-left: 2px;\n" +
                "            padding-right: 5px;\n" +
                "            color: #777777;\n" +
                "        }\n" +
                "\n" +
                "        .test {\n" +
                "            background-color: #eeeeee;\n" +
                "            padding-left: 2em;\n" +
                "        }\n" +
                "\n" +
                "        .successIndicator {\n" +
                "            float: right;\n" +
                "            font-family: monospace;\n" +
                "            font-weight: bold;\n" +
                "            padding-right: 2px;\n" +
                "            color: #44aa44;\n" +
                "        }\n" +
                "\n" +
                "        .skipIndicator {\n" +
                "            float: right;\n" +
                "            font-family: monospace;\n" +
                "            font-weight: bold;\n" +
                "            padding-right: 2px;\n" +
                "            color: #ffaa00;\n" +
                "        }\n" +
                "\n" +
                "        .failureIndicator {\n" +
                "            float: right;\n" +
                "            font-family: monospace;\n" +
                "            font-weight: bold;\n" +
                "            padding-right: 2px;\n" +
                "            color: #ff4444;\n" +
                "        }\n" +
                "\n" +
                "        /*左侧用例列表的样式结束*/\n" +
                "\n" +
                "        /*中间DIV样式*/\n" +
                "        #div_center {\n" +
                "            float: left;\n" +
                "            width: 2%;\n" +
                "            height: 100%;\n" +
                "        }\n" +
                "\n" +
                "        /*右侧报告正文的样式*/\n" +
                "        #div_right {\n" +
                "            float: left;\n" +
                "            width: 83%;\n" +
                "            height: 100%;\n" +
                "        }\n" +
                "\n" +
                "        #right-panel {\n" +
                "            margin-left: -40;\n" +
                "            right: 0;\n" +
                "            top: 0;\n" +
                "            bottom: 0;\n" +
                "            left: 11px;\n" +
                "            overflow: auto;\n" +
                "            background: white\n" +
                "        }\n" +
                "\n" +
                "        #right-panel .group {\n" +
                "            font-size: 15px;\n" +
                "            font-weight: bold;\n" +
                "            line-height: 16px;\n" +
                "            padding: 0 0 0 18px;\n" +
                "            counter-reset: assertion;\n" +
                "            background-repeat: repeat-x;\n" +
                "            background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAQCAYAAADXnxW3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUkDq8pxjkAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAADdJREFUCNdVxrERwDAMAzGK0v47eS6Z927SpMFBAAbkvSvnRk5+7K5cVfLMyN39bWakJAjA5xw9R94jN3tVhVEAAAAASUVORK5CYII=)\n" +
                "        }\n" +
                "\n" +
                "        #right-panel .zebra {\n" +
                "            background-repeat: repeat;\n" +
                "            padding: 0 0 0 18px;\n" +
                "            background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAmCAYAAAAFvPEHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBYWFlNztEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAABdJREFUCNdjYKAtePv5338mBgYGBpoQAGy1BAJlb/y6AAAAAElFTkSuQmCC)\n" +
                "        }\n" +
                "\n" +
                "        #right-panel .data {\n" +
                "            line-height: 19px;\n" +
                "            white-space: nowrap\n" +
                "        }\n" +
                "\n" +
                "        #right-panel pre.data {\n" +
                "            white-space: pre\n" +
                "        }\n" +
                "\n" +
                "        #right-panel tbody.failure {\n" +
                "            color: red\n" +
                "        }\n" +
                "\n" +
                "        #right-panel td.key {\n" +
                "            min-width: 108px\n" +
                "        }\n" +
                "\n" +
                "        #right-panel td.delimiter {\n" +
                "            min-width: 18px\n" +
                "        }\n" +
                "\n" +
                "\n" +
                "        .arguments {\n" +
                "            font-family: Lucida Console, Monaco, Courier New, monospace;\n" +
                "            font-weight: bold;\n" +
                "        }\n" +
                "    </style>\n";
        return style;
    }
}



  • 6.javaScript 的类
package org.programmerplanet.ant.taskdefs.jmeter;

/**
 * Created by jiaou on 2016/12/26.
 */
public class ReportJavaScript {
    public static String getJavaScript() {
        String javaScript = "<script language=\"JavaScript\">\n" +
                "        /*\n" +
                "         * 展开关闭左侧用例列表方法\n" +
                "         * */\n" +
                "        function toggleElement(elementId, displayStyle) {\n" +
                "            var current = getStyle(elementId, 'display');\n" +
                "            document.getElementById(elementId).style.display = (current == 'none' ? displayStyle : 'none');\n" +
                "        }\n" +
                "        function getStyle(elementId, property) {\n" +
                "            var element = document.getElementById(elementId);\n" +
                "            return element.currentStyle ? element.currentStyle[property] : document.defaultView.getComputedStyle(element, null).getPropertyValue(property);\n" +
                "        }\n" +
                "\n" +
                "\n" +
                "        function toggle(toggleId) {\n" +
                "            var toggle;\n" +
                "            if (document.getElementById) {\n" +
                "                toggle = document.getElementById(toggleId);\n" +
                "            } else if (document.all) {\n" +
                "                toggle = document.all[toggleId];\n" +
                "            }\n" +
                "            toggle.textContent = toggle.innerHTML == '\\u25b6' ? '\\u25bc' : '\\u25b6';\n" +
                "        }\n" +
                "        /*\n" +
                "         * 删除左面菜单所有子元素\n" +
                "         * */\n" +
                "        function deleteAllTestBody(testTbody) {\n" +
                "            var trArray = testTbody.childNodes;\n" +
                "            var length = trArray.length;\n" +
                "            for (var i = 0; i < length; i++) {\n" +
                "                try {\n" +
                "                    var nodeName = trArray[i].nodeName;\n" +
                "                    if (nodeName == \"TR\") {\n" +
                "                        testTbody.removeChild(trArray[i]);\n" +
                "                    }\n" +
                "                } catch (e) {\n" +
                "\n" +
                "                }\n" +
                "            }\n" +
                "        }\n" +
                "        /*\n" +
                "         * 根据状态显示当前的用例\n" +
                "         * */\n" +
                "        function showCaseType(obj) {\n" +
                "            var status = \"successIndicator\";\n" +
                "            status = obj.value;\n" +
                "            var testTbody = document.getElementById(\"tests-0\");\n" +
                "            deleteAllTestBody(testTbody);\n" +
                "            var allSpan = document.getElementById(\"allSpan\");\n" +
                "            var spanDivList = allSpan.getElementsByTagName(\"div\");\n" +
                "            var htmlTr = \"\";\n" +
                "            var index = 0;\n" +
                "            for (var i = 0; i < spanDivList.length; i++) {\n" +
                "                var div = spanDivList[i];\n" +
                "                try {\n" +
                "                    var span = div.getElementsByTagName(\"span\")[0];\n" +
                "                    var a = div.getElementsByTagName(\"a\")[0];\n" +
                "                    a.setAttribute(\"id\", \"Case\" + index);\n" +
                "                    var spanOuterHTML = span.outerHTML;\n" +
                "                    var aOuterHTML = a.outerHTML;\n" +
                "                    if (status == \"all\") {\n" +
                "                        htmlTr += \"<tr><Td class='test'>\" + spanOuterHTML + aOuterHTML + \"</Td></tr>\"\n" +
                "                    } else {\n" +
                "                        var spanClassName = span.className;\n" +
                "                        if (status == spanClassName) {\n" +
                "                            htmlTr += \"<tr><Td class='test'>\" + spanOuterHTML + aOuterHTML + \"</Td></tr>\"\n" +
                "                        }\n" +
                "                    }\n" +
                "                    index++;\n" +
                "                } catch (E) {\n" +
                "\n" +
                "                }\n" +
                "            }\n" +
                "            testTbody.innerHTML = htmlTr;\n" +
                "        }\n" +
                "        /*\n" +
                "         * 根据用例名称查询测试用例\n" +
                "         * */\n" +
                "        function searchCase() {\n" +
                "            var searchText = document.getElementById(\"searchText\").value;\n" +
                "            var table = document.getElementById(\"suites\");\n" +
                "            var aList = table.getElementsByTagName(\"a\");\n" +
                "            if (aList.length > 0) {\n" +
                "                var index = 0;\n" +
                "                for (var i = 0; i < aList.length; i++) {\n" +
                "                    var obj = aList[i];\n" +
                "                    var text = obj.text;\n" +
                "                    if (searchText == text) {\n" +
                "                        showDetail(obj);\n" +
                "                        break;\n" +
                "                    }\n" +
                "                    console.info(text);\n" +
                "                    index++;\n" +
                "                }\n" +
                "                if (index == aList.length) {\n" +
                "                    alert(\"当前状态下没有该用例\")\n" +
                "                }\n" +
                "            }\n" +
                "        }\n" +
                "        /*\n" +
                "         * 点击左侧用例按钮显示当前用例到正文\n" +
                "         * */\n" +
                "        function showDetail(obj) {\n" +
                "            var caseId = obj.id;\n" +
                "            document.getElementById(\"currentCaseId\").value = caseId;\n" +
                "            var caseName = obj.text;\n" +
                "            document.getElementById(\"caseName\").innerHTML = \"当前用例:\" + caseName;\n" +
                "            var parentCaseId = \"parent\" + caseId;\n" +
                "            var allCaseNum = document.getElementById(\"allCaseNum\").value;\n" +
                "            for (var i = 0; i < allCaseNum; i++) {\n" +
                "                var div = \"parentCase\" + i;\n" +
                "                if (div == parentCaseId) {\n" +
                "                    document.getElementById(div).style.display = \"inline\"\n" +
                "                } else {\n" +
                "                    document.getElementById(div).style.display = \"none\"\n" +
                "                }\n" +
                "            }\n" +
                "        }\n" +
                "    </script>";

        return javaScript;
    }
}


  • 7.邮件的类
package org.programmerplanet.ant.taskdefs.jmeter;

import com.sun.mail.util.MailSSLSocketFactory;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Created with IntelliJ IDEA.
 * User: jiaou
 * Date: 14-8-19
 * Time: 下午2:55
 * To change this template use File | Settings | File Templates.
 */
public class SendEmail {
//    private static Logger logger = Logger.getLogger(SendEmail.class);

    // JavaMail需要Properties来创建一个session对象。它将寻找字符串"mail.smtp.host",属性值就是发送邮件的主机
    public static void sendEmail(MailObj mailObj) throws Exception {
        Properties properties = new Properties();

        // 开启debug调试
//        properties.setProperty("mail.debug", "true");
        // 发送服务器需要身份验证
        properties.setProperty("mail.smtp.auth", Boolean.toString(mailObj.isSsl()));
        // 设置邮件服务器主机名
        properties.setProperty("mail.host", mailObj.getHost());
        // 发送服务器端口
        if (mailObj.getPort() != null && !mailObj.getPort().equalsIgnoreCase("")) {
            properties.setProperty("mail.smtp.port", mailObj.getPort());
        }
        // 发送邮件协议名称
        properties.setProperty("mail.transport.protocol", "smtp");

        //开启了 SSL 加密 QQ邮箱要加上
        if (mailObj.getFrom().endsWith("@qq.com")) {
            MailSSLSocketFactory sf = new MailSSLSocketFactory();
            sf.setTrustAllHosts(true);
            properties.put("mail.smtp.ssl.enable", "true");
            properties.put("mail.smtp.ssl.socketFactory", sf);
        }
        final String userName = mailObj.getName();
        final String password = mailObj.getPassword();
        /*
         * 在 JavaMail 中,可以通过 extends Authenticator 抽象类,在子类中覆盖父类中的
         * getPasswordAuthentication() 方法,就可以实现以不同的方式来进行登录邮箱时的用户身份认证。JavaMail
         * 中的这种设计是使用了策略模式(Strategy
         */
        MimeMessage message = new MimeMessage(Session.getInstance(properties,
                new Authenticator() {
                    public PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(//设置发送帐号密码
                                userName, password);
                    }
                }));
        // 设置邮件的属性

        // 设置邮件的发件人
        message.setFrom(new InternetAddress(mailObj.getFrom()));

        String sendTo = mailObj.getToAddress();
        String sendToCC = mailObj.getToCc();

        // 设置邮件的收件人 cc表示抄送 bcc 表示暗送
        if (null != sendTo) {
            InternetAddress[] senderList = new InternetAddress()
                    .parse(sendTo);
            message.setRecipients(Message.RecipientType.TO, senderList);
        } else {
            return;
        }
        if (null != sendToCC) {
            InternetAddress[] iaCCList = new InternetAddress()
                    .parse(sendToCC);
            message.setRecipients(Message.RecipientType.CC, iaCCList);
        }
        String subject = mailObj.getMailTitle();
        if (subject.equalsIgnoreCase("")) {
            // 设置邮件的主题
            message.setSubject("自动化测试报告");
        } else {
            message.setSubject(subject);
        }

        // 创建邮件的正文
        MimeBodyPart text = new MimeBodyPart();
        // setContent(“邮件的正文内容”,”设置邮件内容的编码方式”)
        text.setContent(mailObj.getMailContent() + "<img src='cid:b'>",
                "text/html;charset=UTF-8");

        // 点到点的发送
        // 一对多发送只要改一个地方如下:


        // 创建图片
        MimeBodyPart img = new MimeBodyPart();
        /*
         * JavaMail API不限制信息只为文本,任何形式的信息都可能作茧自缚MimeMessage的一部分.
         * 除了文本信息,作为文件附件包含在电子邮件信息的一部分是很普遍的. JavaMail
         * API通过使用DataHandler对象,提供一个允许我们包含非文本BodyPart对象的简便方法.
         */



        // 关系 正文和图片的
        MimeMultipart mm = new MimeMultipart();
        mm.addBodyPart(text);

        // 创建图片的一个表示用于显示在邮件中显示
        File pngFile = new File(mailObj.getReportPath() + "\\report.png");
        if (pngFile.exists()) {
            DataHandler dh = new DataHandler(new FileDataSource(pngFile.getAbsolutePath()));//图片路径
            img.setDataHandler(dh);
            img.setContentID("passRate");
            mm.addBodyPart(img);
        }
//        mm.addBodyPart(img2);
        mm.setSubType("related");// 设置正文与图片之间的关系

        // 创建附件
        File file = new File(mailObj.getHtmlPath());
        if (file.exists()) {
            MimeBodyPart attch = new MimeBodyPart();
            DataHandler dh1 = new DataHandler(new FileDataSource(file.getAbsolutePath()));
            attch.setDataHandler(dh1);
            String filename1 = dh1.getName();
            //   MimeUtility 是一个工具类,encodeText()//用于处理附件字,防止中文乱码问题
            attch.setFileName(MimeUtility.encodeText(filename1));
            mm.addBodyPart(attch);
        }

        // 图班与正文的 body
        MimeBodyPart all = new MimeBodyPart();
        all.setContent(mm);
        // 附件与正文(text 和 img)的关系
        MimeMultipart mm2 = new MimeMultipart();
        mm2.addBodyPart(all);
        mm2.setSubType("mixed");// 设置正文与附件之间的关系

//        mm3.setSubType("related");// 设置正文与图片之间的关系
        message.setContent(mm);
        message.saveChanges(); // 保存修改

        Transport.send(message);// 发送邮件
//        logger.info("邮件发送成功");
        System.out.println("邮件发送成功");
    }
}


  • 8.断言的类
package org.programmerplanet.ant.taskdefs.jmeter;

/**
 * Created with IntelliJ IDEA.
 * User: jiaou
 * Date: 16-11-7
 * Time: 下午2:35
 * To change this template use File | Settings | File Templates.
 */
public class AssertionResult {
    private String name;
    private boolean failure;
    private boolean error;
    private String failureMessage;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isFailure() {
        return failure;
    }

    public void setFailure(boolean failure) {
        this.failure = failure;
    }

    public boolean isError() {
        return error;
    }

    public void setError(boolean error) {
        this.error = error;
    }

    public String getFailureMessage() {
        return failureMessage;
    }

    public void setFailureMessage(String failureMessage) {
        this.failureMessage = failureMessage;
    }
}


  • 9.OS 类,用于获取 IP
package org.programmerplanet.ant.taskdefs.jmeter;

import java.awt.*;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

/**
 * Created with IntelliJ IDEA.
 * User: jiaou
 * Date: 16-7-23
 * Time: 下午10:51
 * To change this template use File | Settings | File Templates.
 */
public class OS {
    public static final int LINUX = 1;
    public static final int WINDOWS = 0;
    public static boolean runModeBaseBS = false;

    private static boolean osIsMacOsX;
    private static boolean osIsWindows;
    private static boolean osIsWindowsXP;
    private static boolean osIsWindows2003;
    private static boolean osIsLinux;
    public static String fileSeparator = System.getProperty("file.separator");

    public static void initOS() {
        String os = System.getProperty("os.name").toLowerCase();
        osIsMacOsX = "mac os x".equals(os);
        osIsWindows = os.indexOf("windows") != -1;
        osIsWindowsXP = "windows xp".equals(os);
        osIsWindows2003 = "windows 2003".equals(os);
        osIsLinux = "linux".equalsIgnoreCase(os);
    }


    /**
     * 获取本机IP
     */
    public static String getLocalIP() {
        initOS();
        String ip = "";
        try {
            if (isLinux()) {
                Enumeration<?> e1 = (Enumeration<?>) NetworkInterface
                        .getNetworkInterfaces();
                while (e1.hasMoreElements()) {
                    NetworkInterface ni = (NetworkInterface) e1.nextElement();
                    if (!ni.getName().equals("eth0")) {
                        continue;
                    } else {
                        Enumeration<?> e2 = ni.getInetAddresses();
                        while (e2.hasMoreElements()) {
                            InetAddress ia = (InetAddress) e2.nextElement();
                            if (ia instanceof Inet6Address)
                                continue;
                            ip = ia.getHostAddress();
                        }
                        break;
                    }
                }
            } else {
                ip = InetAddress.getLocalHost().getHostAddress().toString();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        if(ip==null||ip.equalsIgnoreCase("")){
            ip=getCentOsIp();
        }
        return ip;
    }
}
共收到 29 条回复 时间 点赞

这貌似改的工程有点大?我之前是直接在持续集成系统里发邮件的,然后加一个 xsl,除了生成最终报告多一个生成 html 片段 (测试概要) 的子任务,引入到邮件内容中,明天电脑上看看

taki #11 · 2017年03月26日 Author
北溟 回复

用 xsl 也可以弄,我没抽出来模板

taki #10 · 2017年03月26日 Author
北溟 回复

抽出来模板有些东西也要重写的

不错不错

—— 来自 TesterHome 官方 安卓客户端

内容挺不错的,小小建议下,代码建议还是放关键部分即可,如果确实想给出比较完整的代码,更建议放到 github 然后文末给个链接查看。否则代码太多,容易看不清文章的层级。

taki #7 · 2017年03月27日 Author
陈恒捷 回复

嗯,有 github 一直没用,后续再有东西放到 git 上

Baozhida jmeter+ant+jenkins 接口自动化报告模版 中提及了此贴 03月27日 17:10

核心代码太核心,复制之后,还少很多东西

能否发我一份完整的源码或者 jar 包么,感谢!
qqmail :280745188@qq.com

同求完整的 jar 包 QQ869091814

建议楼主 将这些 html 内容放到一个静态模板文件里面,然后把需要每次替换的数据变成 关键字,再每次读取出来根据关键字再 replace。

能不能把代码放到 github 然后给个链接查看,我们也好参考详细完整代码

楼主没有考虑过 使用 jmeter + maven 的方式吗? github 有写好的插件

匿名 #14 · 2017年05月15日

lz 想问一下 1 jar 的包不是编译好的嘛不能再修改代码了吧
那么你怎么去修改 ant-jmeter-1.1.1.jar 这个文件的?

taki #17 · 2017年05月16日 Author

源码重新编译

匿名 #16 · 2017年05月16日
taki 回复

谢了 不过我发现这玩意直接有源代码 不过藏得很隐蔽。。。还是给我找到了

匿名 #23 · 2017年05月16日

不好意思啊 问一下 MailObj 这个类是哪来的?javamail 和 ant-javamail 里好像没有

匿名 #19 · 2017年05月16日

List 这个类 和 isLinux() 这个方法好像也没有

请教下我把代码导入进去了,但是几个 class 都存在报错。
1)SendEmail.java 有很多报错,不晓得是不是 jar 包版本不匹配;
2)ReportTask.java 和 Report.java 里的 ReportCase 不存在报错;
3)OS.java 里的 isLinux() 方法 不存在报错;

楼主能帮忙解答下这些问题吗?估计很多想进行报告优化的同学都遇到过这些问题。如果其他同学有解决过这些问题,请指点下:
85628284@qq.com

匿名 #11 · 2017年06月15日
taki 回复

谢谢楼主开源 不过我已经自己把剩下的代码补齐了 还是谢谢啦

taki 回复

我按照你的思路,自己用 javamail 写的发邮件

非常感谢楼主开源,我已经调通成功可以发送邮件和生产报告。
但是生成的 HTML 报告在各种浏览器上打开都是空白的,点击刷新能看到报告页面数据,但仅仅也是一闪而过又变成空白的了😭
楼主之前有遇到过这个问题吗?

taki #8 · 2017年06月30日 Author
ben liao 回复

没有,先看本地是否能正确打开

LZ 的报告上,运行时间是负的,这个问题有解决么?
有没有考虑,在搜索框中检索出耗时比较长的用例

taki jmeter 接口自动化测试方案一 (数据驱动版本) 中提及了此贴 03月20日 20:32

sendmai 类的 导入 MailSSLSocketFactory 报错怎么解决...

运行那个类都是提示找不到 org.programmerplanet.ant.taskdefs.jmeter.** 楼主知道是什么原因么

taki #4 · 2018年11月23日 Author
回复

少包

taki #2 · 2018年11月23日 Author
isenven 回复

暂时没有,呵呵,这个做的属于演示性质,没做那么完善

isenven 回复

遍历 httpSample 节点取 start time 和 endtime 时 修改一下就可以了

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册