欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

一键测试生成单元测试报告testNG+BeautifulReport

程序员文章站 2022-03-29 21:29:36
一.背景1.公司为规范团队的开发,涉及到组下员工的代码开发规范,日志打印规范,预警机制等。其中一项是规范团队的开发的单元测试。为了方便核检查小组开发需求的单元测试而整合的单元测试报告,测试框架用的测试框架为TESTNG,模板用的是二.搭建三.使用四.模板......

一.背景

1.公司为规范团队的开发,涉及到组下员工的代码开发规范,日志打印规范,预警机制等。其中一项是规范团队的开发的单元测试。为了方便核检查小组开发需求的单元测试而整合的单元测试报告,测试框架用的测试框架为TESTNG,模板用的是BeautifulReport。先看一下展示的效果
一键测试生成单元测试报告testNG+BeautifulReport

二.搭建

1.使用的pom

这里我根据个人需求,把mongodb的包排除了,是因为extentreports包里面的一些异常信息截图功能会使用到mongodb,项目因为没有用到mongodb,这里不排除单侧会抛异常,但不影响使用。

<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<version>6.14.3</version>
		</dependency>
		<dependency>
			<groupId>com.aventstack</groupId>
			<artifactId>extentreports</artifactId>
			<version>3.0.7</version>
			<exclusions>
				<exclusion>
					<groupId>org.mongodb</groupId>
					<artifactId>mongodb-driver</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

2.监听器

我在项目下src/test/java/com/abcnull/listener添加了一个测试报告监听器类 TestReportListener,它继承自 testng 依赖包中的 IReporter。TestReportListener 中其实没用到太多 extentreports 依赖中的 API,只有 Gson 类来自 extentreports 依赖中,基本还是靠着第三步中写好的 BeautifulReport 型式的 html 模板,代码如下
一键测试生成单元测试报告testNG+BeautifulReport
监听器代码如下,代码可根据个人需求自行继续编写优化



import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.testng.*;
import org.testng.xml.XmlSuite;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author abcnull
 * @version 1.0.0
 * @date 2020/1/26
 */
public class TestReportListener implements IReporter {
    // 日期格式化
    private static Date date = new Date();
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd,HH点mm分ss秒");
    private static String reportdate = simpleDateFormat.format(date);
    private static String getReportName = "测试报告-" + reportdate;
    // 定义html模板所在路径
    private String templatePath = this.getClass().getResource("/").getPath() + "report/template.html";
    // 定义报告生成的路径
    private String reportDirPath = System.getProperty("user.dir") + File.separator + "target" + File.separator + "test-output" + File.separator + "report";
    private String reportPath = reportDirPath + File.separator + getReportName + ".html";
    private String name = "DemoTest";
    private int testsPass;
    private int testsFail;
    private int testsSkip;
    private String beginTime;
    private long totalTime;
    private String project = "单元测试报告";

    @Override
    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
        List<ITestResult> list = new ArrayList<ITestResult>();
        for (ISuite suite : suites) {
            Map<String, ISuiteResult> suiteResults = suite.getResults();
            for (ISuiteResult suiteResult : suiteResults.values()) {
                ITestContext testContext = suiteResult.getTestContext();
                IResultMap passedTests = testContext.getPassedTests();
                testsPass = testsPass + passedTests.size();
                IResultMap failedTests = testContext.getFailedTests();
                testsFail = testsFail + failedTests.size();
                IResultMap skippedTests = testContext.getSkippedTests();
                testsSkip = testsSkip + skippedTests.size();
                IResultMap failedConfig = testContext.getFailedConfigurations();
                list.addAll(this.listTestResult(passedTests));
                list.addAll(this.listTestResult(failedTests));
                list.addAll(this.listTestResult(skippedTests));
                list.addAll(this.listTestResult(failedConfig));
            }
        }
        this.sort(list);
        this.outputResult(list);

    }

    private ArrayList<ITestResult> listTestResult(IResultMap resultMap) {
        Set<ITestResult> results = resultMap.getAllResults();
        return new ArrayList<ITestResult>(results);
    }

    private void sort(List<ITestResult> list) {
        Collections.sort(list, new Comparator<ITestResult>() {
            @Override
            public int compare(ITestResult r1, ITestResult r2) {
                return r1.getStartMillis() < r2.getStartMillis() ? -1 : 1;
            }
        });
    }

    public long getTime() {
        return totalTime;
    }

    private void outputResult(List<ITestResult> list) {
        try {
            List<ReportInfo> listInfo = new ArrayList<ReportInfo>();
            int index = 0;
            for (ITestResult result : list) {
                String testName = result.getTestContext().getCurrentXmlTest().getName();
                if (index == 0) {
                    SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                    beginTime = formatter.format(new Date(result.getStartMillis()));
                    index++;
                }
                long spendTime = result.getEndMillis() - result.getStartMillis();
                totalTime += spendTime;
                String status = this.getStatus(result.getStatus());
                List<String> log = Reporter.getOutput(result);
                for (int i = 0; i < log.size(); i++) {
                    log.set(i, log.get(i).replaceAll("\"", "\\\\\""));
                }
                Throwable throwable = result.getThrowable();
                if (throwable != null) {
                    log.add(throwable.toString().replaceAll("\"", "\\\\\""));
                    StackTraceElement[] st = throwable.getStackTrace();
                    for (StackTraceElement stackTraceElement : st) {
                        log.add(("    " + stackTraceElement).replaceAll("\"", "\\\\\""));
                    }
                }
                ReportInfo info = new ReportInfo();
                info.setName(testName);
                info.setSpendTime(spendTime + "ms");
                info.setStatus(status);
                info.setClassName(result.getInstanceName());
                info.setMethodName(result.getName());
                info.setDescription(result.getMethod().getDescription());
                info.setLog(log);
                listInfo.add(info);
            }
            Map<String, Object> result = new HashMap<String, Object>();
            result.put("testName", this.project);
            result.put("testPass", testsPass);
            result.put("testFail", testsFail);
            result.put("testSkip", testsSkip);
            result.put("testAll", testsPass + testsFail + testsSkip);
            result.put("beginTime", beginTime);
            result.put("totalTime", totalTime + "ms");
            result.put("testResult", listInfo);
            Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
            String template = this.read(reportDirPath, templatePath);
            BufferedWriter output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(reportPath)), "UTF-8"));
            template = template.replace("${resultData}", gson.toJson(result));
            output.write(template);
            output.flush();
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String getStatus(int status) {
        String statusString = null;
        switch (status) {
            case 1:
                statusString = "成功";
                break;
            case 2:
                statusString = "失败";
                break;
            case 3:
                statusString = "跳过";
                break;
            default:
                break;
        }
        return statusString;
    }

    public static class ReportInfo {
        private String name;
        private String className;
        private String methodName;
        private String description;
        private String spendTime;
        private String status;
        private List<String> log;

        public String getName() {
            return name;
        }

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

        public String getClassName() {
            return className;
        }

        public void setClassName(String className) {
            this.className = className;
        }

        public String getMethodName() {
            return methodName;
        }

        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }

        public String getSpendTime() {
            return spendTime;
        }

        public void setSpendTime(String spendTime) {
            this.spendTime = spendTime;
        }

        public String getStatus() {
            return status;
        }

        public void setStatus(String status) {
            this.status = status;
        }

        public List<String> getLog() {
            return log;
        }

        public void setLog(List<String> log) {
            this.log = log;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

    }

    private String read(String reportDirPath, String templatePath) {
        //文件夹不存在时级联创建目录
        File reportDir = new File(reportDirPath);
        if (!reportDir.exists() && !reportDir.isDirectory()) {
            reportDir.mkdirs();
        }
        File templateFile = new File(templatePath);
        InputStream inputStream = null;
        StringBuffer stringBuffer = new StringBuffer();
        try {
            inputStream = new FileInputStream(templateFile);
            int index = 0;
            byte[] b = new byte[1024];
            while ((index = inputStream.read(b)) != -1) {
                stringBuffer.append(new String(b, 0, index));
            }
            return stringBuffer.toString();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}

3.在src/resources下新增testNG.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="单元测试报告">

<!--    添加html监听,这里指向监听器的包路径-->
    <listeners>
        <listener class-name="com.epos.sqdz.listener.TestReportListener"/>
    </listeners>


    <!--    指定某个或某些单元测试类运行-->
    <test verbose="2" preserve-order="true" name="测试登录单元测试">
        <!--传递参数-->
        <parameter name="add1" value="3"/>
        <parameter name="add2" value="2"/>
        <parameter name="result" value="6"/>
        <classes>
            <class name="com.epos.sqdz.service.impl.TestNG2" />
        </classes>
    </test>


<!--    &lt;!&ndash;    1.指定单元测试类里面的某个或某些方法运行。-->
<!--            2. preserve-order 参数用于控制测试用例的执行顺序。如果为:true,-->
<!--               测试用例的顺序为:testCase > testCase1 > testCase2。-->
<!--               如果为:false ,那么默认会按照用例的名称的有字母/数字的顺序执行:testCase1 > testCase2 > testCase3。-->
<!--    &ndash;&gt;-->
<!--    <test verbose="2" preserve-order="true" name="测试执行顺序">-->
<!--        <classes>-->
<!--            <class name="com.epos.sqdz.service.impl.TestNG1" >-->
<!--                <methods>-->
<!--                    <include name="testCase3" />-->
<!--                    <include name="testCase1" />-->
<!--                    <include name="testCase2" />-->
<!--                </methods>-->
<!--            </class>-->
<!--        </classes>-->
<!--    </test>-->



<!--    &lt;!&ndash;    测试传参&ndash;&gt;-->
<!--    <test verbose="2" preserve-order="true" name="测试单元测试传参">-->
<!--        <parameter name="add1" value="3"/>-->
<!--        <parameter name="add2" value="2"/>-->
<!--        <parameter name="result" value="6"/>-->
<!--        <classes>-->
<!--            <class name="com.epos.sqdz.service.impl.TestNG1" >-->
<!--                <methods>-->
<!--                    <include name="testAdd1" />-->
<!--                </methods>-->
<!--            </class>-->
<!--        </classes>-->
<!--    </test>-->
</suite>

4.编写单元测试


import com.epos.sqdz.EposApiApplication;
import com.epos.sqdz.entity.UserBankCardBind;
import com.epos.sqdz.mapper.UserBankCardBindMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.springframework.transaction.annotation.Transactional;
import org.testng.Assert;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

import java.util.List;

@SpringBootTest(classes = EposApiApplication.class)
public class TestNG2  extends AbstractTransactionalTestNGSpringContextTests {

    @Autowired
    private UserBankCardBindMapper userBankCardBindMapper;

    @Test(description = "比较两数相等")
    public void testA() {
        Assert.assertEquals(1, 1);
    }



    @Test(description = "测试查询数据库")
    public void testB() {
        List<UserBankCardBind> bindInfo = userBankCardBindMapper.selectCardInfoByAuthCard("6228430338035659874", "1000000422");
        Assert.assertEquals(1, 1);
    }


    @Test(description = "测试插入数据库")
    @Transactional
    @Rollback(true)// 事务自动回滚,默认是true。可以不写
    public void testC() {
        UserBankCardBind userBankCardBind = new UserBankCardBind();
        userBankCardBind.setAccountCode("-");
        userBankCardBind.setAccountName("-");
        userBankCardBind.setAccountType("-");
        userBankCardBind.setAgentCode("-");
        userBankCardBind.setAgentName("-");
        userBankCardBind.setBankCity("-");
        userBankCardBind.setBankCode("-");
        userBankCardBind.setBankName("-");
        userBankCardBind.setBankProvice("-");
        userBankCardBind.setChangeType("-");
        userBankCardBind.setMhtType("-");
        userBankCardBind.setOldAccountCode("-");
        userBankCardBind.setOldAccountName("-");
        userBankCardBind.setUserId(452451L);
        userBankCardBind.setRecordId(2L);
        int num =  userBankCardBindMapper.insert(userBankCardBind);
        Assert.assertEquals(num, 1);
    }



    @Test(description = "测试方法传参")
    @Parameters({"add1","add2","result"})
    public void testAdd1(int add1, int add2, int result){
        Assert.assertEquals(add1+ add2, result);
    }
}

5.创建html模板

1.模板内容较长,免费免积分下载地址:https://download.csdn.net/download/weixin_43659676/14121325
2.在src/test/resouces/report下创建一个html模板,路径可自定义,搭配调整监听器路径即可,模板内容也可根据需求自定义优化编写
一键测试生成单元测试报告testNG+BeautifulReport

三.使用

直接右键运行xml,便会执行所有单元测试,执行完成后,在target/tets-output/report(路径在监听器可调整)下会生成一个html测试报告,就可以直接打开使用了
一键测试生成单元测试报告testNG+BeautifulReport
一键测试生成单元测试报告testNG+BeautifulReport

本文地址:https://blog.csdn.net/weixin_43659676/article/details/112503490