Selenium 集成 extentreports、beautifulreport、allure 测试报告

哈哈先生 · 2021年01月14日 · 最后由 AlexYou 回复于 2021年03月01日 · 3500 次阅读

extentreports 报告

网上找的各种报告整合
上效果图,当当当。。。带错误截图

pom 信息

<!--测试报告-->
<!-- https://mvnrepository.com/artifact/com.aventstack/extentreports -->

com.aventstack
extentreports
3.1.5
provided

<!-- https://mvnrepository.com/artifact/com.vimalselvam/testng-extentsreport -->

com.vimalselvam
testng-extentsreport
1.3.1


com.squareup.retrofit2
retrofit
2.4.0

共用报告配置文件

projectName.properties

url=http://172.18.2.82:8080
projectName=demo
groupName=demo
testNG 配置
<suite name="测试" parallel="false">
    <!--extentreport-->
    <parameter name="report.config" value="src/main/resources/report/extent-config.xml" />
    <parameter name="system.info" value="com.bigfintax.report.MySystemInfo" />
    <listeners>
        <!--allurereport-->
        <listener class-name="com.bigfintax.report.TestFailListener"/>
        <!--beautifulreport-->
        <listener class-name="com.bigfintax.report.TestReportListener" />
       <!--extentreport-->
        <listener class-name="com.bigfintax.report.MyExtentTestNgFormatter" />

    </listeners>
extent-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
    <configuration>
        <timeStampFormat>yyyy-MM-dd HH:mm:ss</timeStampFormat>
        <!-- report theme -->
        <!-- standard, dark -->
        <theme>dark</theme>

        <!-- document encoding -->
        <!-- defaults to UTF-8 -->
        <encoding>UTF-8</encoding>

        <!-- protocol for script and stylesheets -->
        <!-- defaults to https -->
        <protocol>https</protocol>

        <!-- title of the document -->
        <documentTitle>接口自动化测试报告</documentTitle>

        <!-- report name - displayed at top-nav -->
        <reportName>接口自动化测试报告</reportName>

        <!-- report headline - displayed at top-nav, after reportHeadline -->
        <reportHeadline>接口自动化测试报告</reportHeadline>

        <!-- global date format override -->
        <!-- defaults to yyyy-MM-dd -->
        <dateFormat>yyyy-MM-dd</dateFormat>

        <!-- global time format override -->
        <!-- defaults to HH:mm:ss -->
        <timeFormat>HH:mm:ss</timeFormat>

        <!-- custom javascript -->
        <scripts>
            <![CDATA[
        $(document).ready(function() {

        });
      ]]>
        </scripts>

        <!-- custom styles -->
        <styles>
            <![CDATA[

      ]]>
        </styles>
    </configuration>
</extentreports>

BeautifulRrport 报告

上效果图,当当当。。。

TestReportListener 类

用于监听@Test



/**
 * @author breautiful
 * @version 1.0.0
 * @date 2020/1/26
 */
public class TestReportListener extends ResultListener implements IReporter {

    private static TestConfig tc = new TestConfig("conf/projectName.properties");
    private static Logger log = Logger.getLogger(TestReportListener.class);
    // 日期格式化
    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 = tc.getValue("groupName")+"UI测试报告-" + reportdate;
    // 定义html模板所在路径
//    private String templatePath = this.getClass().getResource("/").getPath() + "report/template.html";
    private String templatePath = System.getProperty("user.dir") + File.separator + "src/main/resources/report/template.html";
    // 定义报告生成的路径
    private String reportDirPath = System.getProperty("user.dir") + File.separator + "target" + File.separator + "test-output" + File.separator + "Breport";
//    private String reportDirPath = System.getProperty("user.dir") + File.separator + "test-output";
//    private String reportDirPath = System.getProperty("user.dir") + File.separator + "test-output" + File.separator + "beautifulReport";;
    private String reportPath = reportDirPath + File.separator + getReportName + "-beautifulReport.html";
    private int testsPass;
    private int testsFail;
    private int testsSkip;
    private String beginTime;
    private int totalTime;
    private String project = tc.getValue("groupName")+"UI测试报告";

    @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 skippedConfig = testContext.getSkippedConfigurations();
                IResultMap failedConfig = testContext.getFailedConfigurations();

                list.addAll(this.listTestResult(passedTests));
                list.addAll(this.listTestResult(failedTests));
                list.addAll(this.listTestResult(skippedTests));
                list.addAll(this.listTestResult(skippedConfig));
                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("yyyy-MM-dd HH:mm:ss");
                    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(formatDuring(spendTime));
                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>();
            log.info("!@#= 运行时间为【" + formatDuring(totalTime) + "】################");
            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", formatDuring(totalTime));
            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;
    }
     //毫秒转换成分钟
    public static String formatDuring(long mss) {
        long days = mss / (1000 * 60 * 60 * 24);
        long hours = (mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60);
        long minutes = (mss % (1000 * 60 * 60)) / (1000 * 60);
        long seconds = (mss % (1000 * 60)) / 1000;
        return  minutes + " 分 "
                + seconds + " 秒 ";
    }

}



# Allure2报告

> 上效果图当当当。。。带错误截图


![](/uploads/photo/2021/ced091b5-a838-40e6-a81b-46ad45c94f08.png!large)


         <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <aspectj.version>1.8.10</aspectj.version>
            <allure.version>2.10.0</allure.version>
         </properties>

        <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-testng</artifactId>
        <version>${allure.version}</version>
        </dependency>

         <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-java-commons</artifactId>
            <version>2.10.0</version>
        </dependency>
        <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.20</version>
                    <configuration>
                        <argLine>
                            -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                        </argLine>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>org.aspectj</groupId>
                            <artifactId>aspectjweaver</artifactId>
                            <version>${aspectj.version}</version>
                        </dependency>
                    </dependencies>
                </plugin>
            </plugins>
        </build>
            <reporting>
                <excludeDefaults>true</excludeDefaults>
                <plugins>
                    <plugin>
                        <groupId>io.qameta.allure</groupId>
                        <artifactId>allure-maven</artifactId>
                        <version>2.10.0</version>
                        <configuration>
                            <reportVersion>${allure.version}</reportVersion>
                        </configuration>
                    </plugin>
                </plugins>
            </reporting>



> ##### allure.properties文件
>
> ````properties
> allure.results.directory=target/allure-results
> allure.link.issue.pattern=https://example.org/issue/{}
> allure.link.tms.pattern=https://example.org/tms/{}
> allure.link.issue.pattern=http://jira.XXX.com/browse/{}
> allure.link.tms.pattern=http://testlink.XXX.com/{}
> ````
>
> 


##### TestFailListener类

```java
package com.bigfintax.report;

import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;

import com.bigfintax.action.BaseAction;

import io.qameta.allure.Attachment;

/**
 * allure 测试报告监听
 * @author Administrator
 *
 */
public class TestFailListener extends TestListenerAdapter {

    @Override
    public void onTestFailure(ITestResult result) {
        takePhoto();
    }

    @Attachment(value = "screen shot",type = "image/png")
    public byte[]  takePhoto(){
        byte[] screenshotAs = ((TakesScreenshot)BaseAction.getDriver()).getScreenshotAs(OutputType.BYTES);
        return screenshotAs;
    }

}

allure 注解使用实例:
//用例编号 @TmsLink("562") 
//bug编号 @Issue("4042")
 /** //bug严重等级,优先级,包含blocker, critical, normal, minor, trivial
 * 几个不同的等级 @Severity(SeverityLevel.TRIVIAL)
 * //用例描述 @Description("测试一个流程,用作回归冒烟测试") /**
 * 功能块,具有相同feature或astory的用例将规整到相同模块下,执行时可用于筛选
 * 
 * @Story("查询场景-正向查询功能")
 * 
 */ 
 @Test(description = "验证百度查询功能", dataProvider = "testDemo")
     @Listeners({ TestFailListener.class })
public class testcase extends BaseCase {
    private Logger log = LoggerFactory.getLogger(this.getclass());
    BgyAction bgyaction;

    @Issue("00")
    @TmsLink("00")
    @Story("用户登录")
    @Description("用户登录")
    @Severity(SeverityLevel.TRIVIAL)
    @Test(description = "用户登录")
    public void test00_login() {
        log.info("------------------登录用例开始执行------------------");
        System.setProperty("webdriver.chrome.driver", "chromedriver.exe");
        WebDriver driver = new ChromeDriver();
        driver.get("http://www.baidu.com");
        driver.findElement(By.linkText("登录")).click();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }                                                                                                                                                             
        driver.findElement(By.id("TANGRAM__PSP_10__footerULoginBtn")).click();
        driver.findElement(By.id("TANGRAM__PSP_10__userName")).clear();
        driver.findElement(By.id("TANGRAM__PSP_10__userName")).sendKeys("12738749817");
        driver.findElement(By.id("TANGRAM__PSP_10__password")).clear();
        driver.findElement(By.id("TANGRAM__PSP_10__password")).sendKeys("2222222222");
        driver.findElement(By.id("TANGRAM__PSP_10__submit")).click();
        //因为有验证码,我们以出现验证码为准,做为断言
        if(driver.findElement(By.id("TANGRAM__PSP_10__error")).isDisplayed()) {
            System.out.println("登录成功");
        }else {
            Assert.fail("断言失败");
        }

    }
        log.info("------------------登录用例执行成功------------------");
    }
共收到 8 条回复 时间 点赞

啥几把玩意,代码辣么长

想把代码都附上的话,建议直接 github 建个项目放上去,然后文章里分享项目地址和关键代码就好?

这代码篇幅太长了,没啥读到尾的欲望。。。

哈哈先生 关闭了讨论 01月15日 09:37
哈哈先生 重新开启了讨论 01月15日 13:41
陈恒捷 回复

第一次发,见谅

pom 显示不完整,用 MarkDown 被标记一下

看着不错,很全面,文档有空再梳理一下,把代码都完整展示了

AlexYou 回复

显示完了就丢不上去了

哈哈先生 回复

要不分两篇?

仅楼主可见
11楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册