【致敬女神】HTMLReport应用之Unittest+Python+Selenium+HTMLReport项目自动化测试实战

news2025/7/16 3:00:26

HTMLReport应用之Unittest+Python+Selenium+HTMLReport项目自动化测试实战

  • 1 测试框架结构
  • 2 技术栈
  • 3 实现思路
    • 3.1 使用HtmlTestRunner
    • 3.2 使用HTMLReport
  • 4 TestRunner参数说明
    • 4.1 源码
    • 4.2 参数说明
  • 5 框架代码
    • 5.1 common/reportOut.py
    • 5.2 common/sendMain.py
    • 5.3 report
      • 5.3.1 xxx.html
      • 5.3.2 xxx.log
      • 5.3.3 xxx.xml
    • 5.4 testcase
    • 5.5 main.py
  • 6 运行结果


『IT女神-仅以此文致敬 女性开发者』

她们在不同领域熠熠生辉,创造了无限的可能,成为了IT发展进步中不可或缺的角色。

她们在AI代码的世界里让梦想生根、开花。

不分年龄、无论背景,她们释放着热情与专业,用技术为自己代言。

致敬每一位IT女神,愿她们永远美丽、永远绽放自己独特的魅力之花。

同时,在这特殊的节日祝愿所有的女性朋友梦想成真!


注意事项:
【本文字数包含代码38175字,建议慢慢看~哈哈哈】
1、以下仅为举例,具体以自身实际项目为准;
2、以下内容重点是介绍HTMLReport的应用,并不是说明如何搭建框架;
3、如果想了解框架内容,可移步博主有关测试框架的系列文章;
4、写了一个用例,用例中没有加断言,只是为了生成测试报告,可以忽略;
5、用例故意写错了3个,1个通过,是为了生成测试报告数据。

1 测试框架结构

在这里插入图片描述
在这里插入图片描述

目录/脚本说明
common/reportOut.py这是是用HTMLReport生成报告的
common/sendMain.py这个是用来发邮件,本次演示可要可不要
report是存放测试报告的,里边有3个文件,由HTMLReport自动生成
testcase存放测试用例的
main.py框架主入口

2 技术栈

技术版本及说明
PythonV3.x(本文为3.7)===编程语言支撑
SeleniumV3.141.0 ===UI元素、控件的识别、定位,以及浏览器控制等
HTMLReport生成Html测试报告
UnittestPython自带===自动化测试框架
SmtplibPython自带===邮件服务
emailPython自带===邮件服务
osPython自带===系统模块
PyCharmCommunity 2020.2汉化版
操作系统Windows10旗舰版64位
其它后续补充

3 实现思路

  • 这里具体就是把原来生成HtmlTestRunner改为HTMLReport

3.1 使用HtmlTestRunner

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2022/3/7
# 文件名称:reportOut.py
# 作用:封装测试报告功能

import time
import unittest
from common import HTMLTestRunner    # 引入导入的报告模板


def report_out(test_dir, report_dir, name_project):
    '''
    :test_dir: 用例路径
    :report_dir : 报告路径
    :name_project : 项目名称=>用于报告命名及描述
    :return: 无
    '''

    now = time.strftime("%Y_%m_%d %H_%M_%S")
    discover = unittest.defaultTestLoader.discover(test_dir,pattern='test*.py')      # 加载测试用例
    report_name = report_dir + now + '-' + name_project+'_test_report.html'          # 报告名称
    with open(report_name,'wb') as f:                                                # 运行用例生成测试报告
        runner = HTMLTestRunner.HTMLTestRunner(stream=f,
                              title=name_project + 'WebUI Auto Testing Report',
                              description=(name_project + U"美多商城UI自动化功能回归测试"),
                              verbosity=2)
        runner.run(discover)
        f.close()

    """
    stream:要操作的文件;
    title:测试报告标题;
    description:报告描述;
    verbosity:报告级别。
    """

3.2 使用HTMLReport

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:reportOut.py
# 作用:封装测试报告功能

import time
import unittest
from HTMLReport import ddt, TestRunner, add_image, no_retry, retry


def report_out(test_dir, report_dir, name_project):
    '''
    :test_dir: 用例路径
    :report_dir : 报告路径
    :name_project : 项目名称=>用于报告命名及描述
    :return: 无
    '''

    now = time.strftime("%Y_%m_%d %H_%M_%S")
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')      # 加载测试用例
    # report_name = now + '-' + name_project + '_test_report.html'          # 报告名称
    test_runner = TestRunner(
        report_file_name=now,
        output_path=report_dir,
        title=name_project,
        description="关于HTMLReport的实际项目应用",
        thread_count=1,
        thread_start_wait=0.1,
        tries=0,
        delay=0,
        back_off=1,
        retry=True,
        sequential_execution=True,
        lang="cn"
    )
    test_runner.run(discover)

4 TestRunner参数说明

4.1 源码

class TestRunner(TemplateMixin, TestSuite):
    """测试执行器"""

    def __init__(self, report_file_name: str = None, log_file_name: str = None, output_path: str = None,
                 title: str = None, description: str = None, tries: int = 0, delay: float = 1, back_off: float = 1,
                 max_delay: float = 120, retry: bool = True, thread_count: int = 1, thread_start_wait: float = 0,
                 sequential_execution: bool = False, lang: str = "cn", image: bool = True, failed_image: bool = False):

4.2 参数说明

参数说明
report_file_name报告文件名,如果未赋值,将采用“test+时间戳”
log_file_name日志文件名,如果未赋值,将采用报告文件名,如果报告文件名也没有,将采用“test+时间戳”
output_path报告保存文件夹名,默认“report
title报告标题,默认“测试报告”
description报告描述,默认“无测试描述”
tries重试次数
delay重试延迟间隔,单位为 秒
back_off扩展每次重试等待时间的乘数
max_delay最大重试等待时间长度,单位为 秒
retry如果为 True 表示所有用例遵循重试规则,False 只针对添加了 @retry 用例有效
thread_count并发线程数量(无序执行测试),默认数量 1
thread_start_wait各线程启动延迟,默认 0 s
sequential_execution是否按照套件添加(addTests)顺序执行, 会等待一个addTests执行完成,再执行下一个,默认 False。如果用例中存在 tearDownClass ,建议设置为True否则 tearDownClass 将会在所有用例线程执行完后才会执行。
lang("cn", "en") 支持中文与英文报告输出,默认采用中文
image默认支持添加图片,False 放弃所有图片添加
failed_imagetrue 只有失败才添加图片,成功用例添加的图片会被删除

5 框架代码

5.1 common/reportOut.py

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:reportOut.py
# 作用:封装测试报告功能

import time
import unittest
from HTMLReport import ddt, TestRunner, add_image, no_retry, retry


def report_out(test_dir, report_dir, name_project):
    '''
    :test_dir: 用例路径
    :report_dir : 报告路径
    :name_project : 项目名称=>用于报告命名及描述
    :return: 无
    '''

    now = time.strftime("%Y_%m_%d %H_%M_%S")
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')      # 加载测试用例
    # report_name = now + '-' + name_project + '_test_report.html'          # 报告名称
    test_runner = TestRunner(
        report_file_name=now,
        output_path=report_dir,
        title=name_project,
        description="关于HTMLReport的实际项目应用",
        thread_count=1,
        thread_start_wait=0.1,
        tries=0,
        delay=0,
        back_off=1,
        retry=True,
        sequential_execution=True,
        lang="cn"
    )
    test_runner.run(discover)

5.2 common/sendMain.py

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:sendMain.py
# 作用:封装邮件服务模块

import time
import smtplib
import getpass
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import email
import os


def send_main(file_path, mail_to='xxx@126.com'):
    mail_from = 'xxxx@126.com'
    f = open(file_path, 'rb')
    mail_body = f.read()
    f.close()

    # msg = email.MIMEMultipart.MIMEMultipart()
    msg = MIMEMultipart()

    # 构造MIMEBase对象做为文件附件内容并附加到根容器
    contype = 'application/octet-stream'
    maintype, subtype = contype.split('/', 1)

    # 读入文件内容并格式化
    data = open(file_path, 'rb')
    # file_msg = email.MIMEBase.MIMEBase(maintype, subtype)
    file_msg = MIMEBase(maintype, subtype)
    file_msg.set_payload(data.read())
    data.close()

    # email.Encoders.encode_base64(file_msg)
    encoders.encode_base64(file_msg)

    # 设置附件头
    basename = os.path.basename(file_path)
    file_msg.add_header('Content-Disposition', 'attachment', filename=basename)
    msg.attach(file_msg)
    print(u'msg 附件添加成功')

    msg1 = MIMEText(mail_body, "html", 'utf-8')
    msg.attach(msg1)

    if isinstance(mail_to, str):
        msg['To'] = mail_to
    else:
        msg['To'] = ','.join(mail_to)
    msg['From'] = mail_from
    msg['Subject'] = u'美多商城UI自动化功能回归测试'
    msg['date'] = time.strftime('%Y-%m-%d-%H_%M_%S')
    print(msg['date'])

    smtp = smtplib.SMTP()
    smtp.connect('smtp.126.com')
    smtp.login('xxx@126.com', 'xxx')  # 登录账号和密码(密码为之前申请的授权码)
    smtp.sendmail(mail_from, mail_to, msg.as_string())
    smtp.quit()
    print('email has send out !')

# if __name__=='__main__':
#     sendmain('../report/2017-08-18-10_18_57_result.html')

5.3 report

5.3.1 xxx.html

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="cn">

<head>
    <title>关于HTMLReport的实际项目应用</title>
    <meta name="generator" content="HTMLReport 刘士 2.3.1"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <style type="text/css" media="screen">
body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    font-size: 14px;
}

pre {
    word-wrap: break-word;
    word-break: break-all;
    overflow: auto;
    white-space: pre-wrap
}

h1 {
    font-size: 16pt;
    color: gray
}

.heading {
    margin-top: 0;
    margin-bottom: 1ex
}

.heading .attribute {
    margin-top: 1ex;
    margin-bottom: 0
}

.heading .description {
    margin-top: 4ex;
    margin-bottom: 6ex
}

a.popup_link:hover {
    color: red
}

.popup_window {
    display: block;
    position: relative;
    left: 0;
    top: 0;
    padding: 10px;
    background-color: #E6E6D6;
    text-align: left;
    font-size: 13px
}

.popup_retry_window {
    padding-left: 50px;
}

#show_detail_line {
    margin-top: 3ex;
    margin-bottom: 1ex
}

#result_table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid #777
}

#header_row {
    color: #fff;
    background-color: #777
}

#result_table td {
    border: 1px solid #777;
    padding: 2px;
}

#result_table td:nth-child(n+2) {
    min-width: 70px;
    width: 100%
}

#result_table td:nth-child(n+3) {
    text-align: center;
}

#result_table td:first-of-type {
    text-align: center;
    min-width: 60px;
}

#total_row {
    font-weight: bold
}

.passClass,
.failClass,
.errorClass,
.skipClass {
    font-weight: bold
}

.passCase {
    background-color: #d0e9c6
}

.failCase {
    background-color: #ebcccc
}

.errorCase {
    background-color: #faf2cc
}

.skipCase {
    background-color: #c4e3f3
}

.hiddenRow {
    display: none
}

.testcase {
    margin-left: 2em
}

#popup {
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    text-align: center;
    display: none
}

#popup .bg {
    background-color: rgba(0, 0, 0, .5);
    width: 100%;
    height: 100%
}

#popup img {
    max-width: 100%;
    max-height: 100%;
    margin: auto;
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
}

img.pic {
    cursor: pointer;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 100%;
    opacity: 1;
    transition: opacity 0.3s;
}

img.pic[data-src] {
    opacity: 0;
}

#wrapper {
    margin: 0 auto;
    border-top: solid 2px #666;
}

#wrapper .lang-en {
    display: none;
}

#wrapper.lang-cn p.lang-cn {
    display: block;
}

#wrapper.lang-cn span.lang-cn {
    display: inline;
}

#wrapper.lang-cn .lang-en {
    display: none;
}

#wrapper.lang-en .lang-cn {
    display: none;
}

#wrapper.lang-en p.lang-en {
    display: block;
}

#wrapper.lang-en span.lang-en {
    display: inline;
}

#lang ul {
    float: right;
    margin: 0;
    padding: 2px 10px 4px 10px;
    background: #666;
    border-radius: 0 0 4px 4px;
    list-style: none;
}

#lang li {
    margin: 0;
    float: left;
    padding: 0 5px;
}

#lang li a {
    display: block;
    width: 24px;
    height: 24px;
    text-indent: -4em;
    overflow: hidden;
    background: transparent url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAHiSURBVHja1Ja/jtNAEMZ/Y2/icBdxXAMSEu/A1dBR0NJQ8CS8AQ0tb4CQgEegPgQFOh7ixJUX4vgSx96ZoUgOO3+KRDgFX7Or0Wg+f7PzeVfcnUMi4cA4OIEAARgAvY5r10AZgOGvl69Gkm4Xk9w3fJTg9f4MDz9+OA3AsSTC4OmThaQE3Bp9w+eRmy+hie2I8us3gOMABFNFkjlW5PTPIvOLAO7YVMjfC/Sd4YuK4nOGuyMiABv7v6pP7mKmACEAeK1YPuPoWU52FgkPUiaf+ngFDjCD+Q/Fproo1vrSbUPuvR4eF7kBwDRi4ynlzxkyUMrvKTZabbrPFb9Jd2qPh7BK4DGiRYFeTJmdC8nAsVKaUes72eOK6Xm2G0GaYhpXCTzPsXEBgOZN8unrktHbAddvAKrdCESwqmoItI74eILlkw0bjt4Zltdg+5hL8NhSYLGmurrCxuPN7Mv951+LAh1kLQWxBlUw68bDGtEqaStQiB0SRMWlbh1yXWPu+MIc/wzTiC0dslBQR0TArfWPwJdr21KyttLKaeJijvmaD0gTMF/z57pPt8W37E1xaylwU0iE5OhON2fgjreMVmuMXC/ntus7QYAT4BFwr+Piv4HL2xstu21Xh4jAXP77V8WfAQAixA0rudAk0AAAAABJRU5ErkJggg==") no-repeat 50% 50%;
}

#lang li a#lang-en {
    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAIWSURBVHja1JY/SBthGIefu1xqS6K20KFDy0kopUiHmphIByUZotRAIZOTWZzFpYtbB0uh6KJTIdQhi9pBSwmmCOpgoUSKFItTh4AU6tCr16Rn5P58XZocDrlYuAz9wfHAcbzv9/2+932/k4QQdFIyHVbHE0iAAlwFgj7HNoG6AoRzudc/A4F/28yL2l7bb269yd9QgJAsS8zMjFIufyWRuHspXqtbnsHrH8oAIQlQJyfzlaGhCNFohJ2dI1Kp/iZ3d49IJvsvvJckmJ197JlACIEsy30KgGUJBgcjFIufSacfsLnpza2tL/x4+qx15fR0Uz84hL8HjG1blEqHJJP9bGx8IpMZ8CSAMIzWq1cUhO24CSzLYWTkPisrH8lm46yuenN9fZ+br156WmRZFgQLjR3YrK2VyWSiFAp7TEw88iTAyZNca4t6e6h/P3EbzTRtxscfks9vk83G27JaPcOuVls/v6o4pltlajo9L1KpebG8vC9isbm2jMXmRDsZhiEAVWn4NTU1ysJCkenpMRYXS55cWnrPcSThUUVhzrquNEeFOjz8vOI4CrXa+aU7+d3p29YJusMYwQD3Drb7AFRd14Xf0nXdtehbfAxdkhG13/5M0HCImiTcPhC2BVIAHMefOWrbCNxYqqZpvlukaVrTIrNye4CK1JH7xpSAXuAOcN3n4KfAceNG62qch4+ygHPpv/+r+DMAXV79BpyNnBoAAAAASUVORK5CYII=");
}

.figure_ul {
    text-align: center;
    padding: 0;
}

.figure_li {
    width: 30em;
    list-style: none;
    display: inline-block;
    vertical-align: baseline;
}

tr {
    height: 2em;
}
</style>
    <script type="text/javascript">
var chartData_cn = [[1, '#1c965b', '通过'], [3, '#ff9800', '错误']];
var chartData_en = [[1, '#1c965b', 'pass'], [3, '#ff9800', 'error']];
function addClass(e, c) {
    if (!isClass(e, c)) {
        if (e.className) {
            e.className = e.className + " " + c;
        } else {
            e.className = c;
        }
    }
}

function delClass(e, c) {
    if (isClass(e, c)) {
        // r = '/(?:^|\s)' + c + '(?!\S)/g';
        let r = new RegExp('(?:^|\s)' + c + '(?!\S)', 'g');
        e.className = e.className.replace(r, '');
    }
}

function isClass(e, c) {
    let r = new RegExp('(?:^|\s)' + c + '(?!\S)');
    return e.className.match(r);
}

function showCase(level) {
    let trs = document.getElementsByTagName("tr");
    for (let i = 0; i < trs.length; i++) {
        let tr = trs[i];
        let id = tr.id;
        if (id.substring(0, 2) === "st") {
            if (level === 4 || level === 3) {
                delClass(tr, 'hiddenRow');
            } else {
                addClass(tr, 'hiddenRow');
            }
        }
        if (id.substring(0, 2) === "ft") {
            if (level === 4 || level === 2) {
                delClass(tr, 'hiddenRow');
            } else {
                addClass(tr, 'hiddenRow');
            }
        }
        if (id.substring(0, 2) === "pt") {
            if (level === 4 || level === 1) {
                delClass(tr, 'hiddenRow');
            } else {
                addClass(tr, 'hiddenRow');
            }
        }
        if (id.substring(0, 2) === "et") {
            if (level === 4 || level === 5 || level === 2) {
                delClass(tr, 'hiddenRow');
            } else {
                addClass(tr, 'hiddenRow');
            }
        }
        if (id.substring(0, 4) === "div_") {
            addClass(tr, 'hiddenRow');
        }
    }
}

function showClassDetail(cid, count) {
    let id_list = Array(count);
    let toHide = 1;
    for (let i = 0; i < count; i++) {
        let tid0 = "t" + cid.substring(1) + "." + (i + 1);
        let tid = "f" + tid0;
        let tr = document.getElementById(tid);
        if (!tr) {
            tid = "p" + tid0;
            tr = document.getElementById(tid);
            if (!tr) {
                tid = "e" + tid0;
                tr = document.getElementById(tid);
                if (tr === null) {
                    tid = "s" + tid0;
                    tr = document.getElementById(tid);
                }
            }
        }
        id_list[i] = tid;
        if (tr.className) {
            toHide = 0;
        }
    }
    for (let i = 0; i < count; i++) {
        let tid = id_list[i];
        if (toHide && tid.indexOf("p") !== -1) {
            addClass(document.getElementById(tid), 'hiddenRow');
        } else {
            delClass(document.getElementById(tid), 'hiddenRow');
        }
    }
    let trs = document.getElementsByTagName("tr");
    for (let i = 0; i < trs.length; i++) {
        let tr = trs[i];
        let id = tr.id;
        if (id.substring(0, 4) === "div_") {
            addClass(tr, 'hiddenRow');
        }
    }
}

function showTestDetail(div_id, count, b) {
    let details_div_s = document.getElementsByName(div_id);
    for (let j = 0; j < details_div_s.length; j++) {
        let details_div = details_div_s[j];
        if (isClass(details_div, 'hiddenRow')) {
            delClass(details_div, 'hiddenRow');
        } else {
            addClass(details_div, "hiddenRow");
        }
    }
    for (let i = 1; i <= count; i++) {
        let details_div_s = document.getElementsByName(div_id + '.' + i);
        for (let j = 0; j < details_div_s.length; j++) {
            let details_div = details_div_s[j];
            if (details_div !== undefined) {
                if (b && isClass(details_div, 'hiddenRow')) {
                    delClass(details_div, 'hiddenRow');
                } else {
                    addClass(details_div, "hiddenRow");
                }
            }
        }
    }
}

function html_escape(s) {
    s = s.replace(/&/g, "&amp;");
    s = s.replace(/</g, "&lt;");
    s = s.replace(/>/g, "&gt;");
    return s;
}

function goChart(dataArr) {

    // 声明所需变量
    var canvas, ctx;
    // 图表属性
    var cWidth, cHeight, cMargin, cSpace;
    // 饼状图属性
    var radius, ox, oy;//半径 圆心
    var tWidth, tHeight;//图例宽高
    var posX, posY, textX, textY;
    var startAngle, endAngle;
    var totleNb;
    // 运动相关变量
    var ctr, numctr, speed;
    //鼠标移动
    var mousePosition = {};

    //线条和文字
    var lineStartAngle, line, textPadding, textMoveDis;

    // 获得canvas上下文
    canvas = document.getElementById("chart");
    if (canvas && canvas.getContext) {
        ctx = canvas.getContext("2d");
    }
    initChart();

    // 图表初始化
    function initChart() {
        // 图表信息
        cMargin = 20;
        cSpace = 40;

        canvas.width = canvas.parentNode.getAttribute("width") * 2;
        canvas.height = canvas.parentNode.getAttribute("height") * 2;
        canvas.style.height = canvas.height / 2 + "px";
        canvas.style.width = canvas.width / 2 + "px";
        cHeight = canvas.height - cMargin * 2;
        cWidth = canvas.width - cMargin * 2;

        //饼状图信息
        radius = cHeight * 2 / 6;  //半径  高度的2/6
        ox = canvas.width / 2 + cSpace;  //圆心
        oy = canvas.height / 2;
        tWidth = 60; //图例宽和高
        tHeight = 20;
        posX = cMargin;
        posY = cMargin;   //
        textX = posX + tWidth + 15
        textY = posY + 18;
        startAngle = endAngle = 90 * Math.PI / 180; //起始弧度 结束弧度
        rotateAngle = 0; //整体旋转的弧度

        //将传入的数据转化百分比
        totleNb = 0;
        new_data_arr = [];
        for (var i = 0; i < dataArr.length; i++) {
            totleNb += dataArr[i][0];
        }
        for (var i = 0; i < dataArr.length; i++) {
            new_data_arr.push(dataArr[i][0] / totleNb);
        }
        totalYNomber = 10;
        // 运动相关
        ctr = 1;//初始步骤
        numctr = 50;//步骤
        speed = 1.2; //毫秒 timer速度

        //指示线 和 文字
        lineStartAngle = -startAngle;
        line = 40;         //画线的时候超出半径的一段线长
        textPadding = 10;  //文字与线之间的间距
        textMoveDis = 200; //文字运动开始的间距
    }

    drawMarkers();

    //绘制比例图及文字
    function drawMarkers() {
        ctx.textAlign = "left";
        for (var i = 0; i < dataArr.length; i++) {
            //绘制比例图及文字
            ctx.fillStyle = dataArr[i][1];
            ctx.fillRect(posX, posY + 40 * i, tWidth, tHeight);
            ctx.moveTo(parseInt(posX) + 0.5, parseInt(posY + 40 * i) + 0.5);
            ctx.font = 'normal 24px 微软雅黑';    //斜体 30像素 微软雅黑字体
            ctx.fillStyle = dataArr[i][1]; //"#000000";
            var percent = dataArr[i][2] + ":" + parseInt(100 * new_data_arr[i]) + "%";
            ctx.fillText(percent, parseInt(textX) + 0.5, parseInt(textY + 40 * i) + 0.5);
        }
    }

    //绘制动画
    pieDraw();

    function pieDraw(mouseMove) {

        for (var n = 0; n < dataArr.length; n++) {
            ctx.fillStyle = ctx.strokeStyle = dataArr[n][1];
            ctx.lineWidth = 1;
            var step = new_data_arr[n] * Math.PI * 2; //旋转弧度
            var lineAngle = lineStartAngle + step / 2;   //计算线的角度
            lineStartAngle += step;//结束弧度

            ctx.beginPath();
            var x0 = ox + radius * Math.cos(lineAngle),//圆弧上线与圆相交点的x坐标
                y0 = oy + radius * Math.sin(lineAngle),//圆弧上线与圆相交点的y坐标
                x1 = ox + (radius + line) * Math.cos(lineAngle),//圆弧上线与圆相交点的x坐标
                y1 = oy + (radius + line) * Math.sin(lineAngle),//圆弧上线与圆相交点的y坐标
                x2 = x1,//转折点的x坐标
                y2 = y1,
                linePadding = ctx.measureText(dataArr[n][2]).width + 10; //获取文本长度来确定折线的长度

            ctx.moveTo(parseInt(x0) + 0.5, parseInt(y0) + 0.5);
            //对x1/y1进行处理,来实现折线的运动
            yMove = y0 + (y1 - y0) * ctr / numctr;
            ctx.lineTo(parseInt(x1) + 0.5, parseInt(yMove) + 0.5);
            if (x1 <= x0) {
                x2 -= line;
                ctx.textAlign = "right";
                ctx.lineTo(parseInt(x2 - linePadding) + 0.5, parseInt(yMove) + 0.5);
                ctx.fillText(dataArr[n][2], x2 - textPadding - textMoveDis * (numctr - ctr) / numctr, y2 - textPadding);
            } else {
                x2 += line;
                ctx.textAlign = "left";
                ctx.lineTo(parseInt(x2 + linePadding) + 0.5, parseInt(yMove) + 0.5);
                ctx.fillText(dataArr[n][2], x2 + textPadding + textMoveDis * (numctr - ctr) / numctr, y2 - textPadding);
            }

            ctx.stroke();

        }

        //设置旋转
        ctx.save();
        ctx.translate(ox, oy);
        ctx.rotate((Math.PI * 2 / numctr) * ctr / 2);

        //绘制一个圆圈
        ctx.strokeStyle = "rgba(0,0,0," + 0.5 * ctr / numctr + ")"
        ctx.beginPath();
        ctx.arc(0, 0, (radius + 20) * ctr / numctr, 0, Math.PI * 2, false);
        ctx.stroke();

        for (var j = 0; j < dataArr.length; j++) {

            //绘制饼图
            endAngle = endAngle + new_data_arr[j] * ctr / numctr * Math.PI * 2; //结束弧度

            ctx.beginPath();
            ctx.moveTo(0, 0); //移动到到圆心
            ctx.arc(0, 0, radius * ctr / numctr, startAngle, endAngle, false); //绘制圆弧

            ctx.fillStyle = dataArr[j][1];
            if (mouseMove && ctx.isPointInPath(mousePosition.x * 2, mousePosition.y * 2)) {
                ctx.globalAlpha = 0.8;
            }

            ctx.closePath();
            ctx.fill();
            ctx.globalAlpha = 1;

            startAngle = endAngle; //设置起始弧度
            if (j == dataArr.length - 1) {
                startAngle = endAngle = 90 * Math.PI / 180; //起始弧度 结束弧度
            }
        }

        ctx.restore();

        if (ctr < numctr) {
            ctr++;
            setTimeout(function () {
                //ctx.clearRect(-canvas.width,-canvas.width,canvas.width*2, canvas.height*2);
                ctx.clearRect(-canvas.width, -canvas.height, canvas.width * 2, canvas.height * 2);
                drawMarkers();
                pieDraw();
            }, speed *= 1.085);
        }
    }

    //监听鼠标移动
    var mouseTimer = null;
    canvas.addEventListener("mousemove", function (e) {
        e = e || window.event;
        if (e.offsetX || e.offsetX == 0) {
            mousePosition.x = e.offsetX;
            mousePosition.y = e.offsetY;
        } else if (e.layerX || e.layerX == 0) {
            mousePosition.x = e.layerX;
            mousePosition.y = e.layerY;
        }

        clearTimeout(mouseTimer);
        mouseTimer = setTimeout(function () {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawMarkers();
            pieDraw(true);
        }, 10);
    });

}

function load() {
    let el_wrapper = document.getElementById('wrapper');
    document.getElementById('lang-cn').onclick = function () {
        el_wrapper.className = 'lang-cn';
        goChart(chartData_cn);
    };
    document.getElementById('lang-en').onclick = function () {
        el_wrapper.className = 'lang-en';
        goChart(chartData_en);
    };

    let nav_lang = (location.hash || '').replace(/#/, '');
    if (nav_lang === 'cn' || nav_lang === 'en') el_wrapper.className = 'lang-' + nav_lang;

    let images = document.getElementsByClassName("pic");
    let lens = images.length;
    let popup = document.getElementById("popup");

    function showBig(src, title, alt) {
        document.getElementById("popup-img").setAttribute("src", src)
        document.getElementById("popup-img").setAttribute("title", title)
        document.getElementById("popup-img").setAttribute("alt", alt)
        popup.style.display = "block";
        popup.style.zIndex = "999999";
    }

    function show(event) {
        event = event || window.event;
        let target = document.elementFromPoint(event.clientX, event.clientY);
        showBig(target.src, target.title, target.alt);
    }

    for (let i = 0; i < lens; i++) images[i].onclick = show;

    popup.onclick = function () {
        document.getElementById("popup-img").removeAttribute("src")
        document.getElementById("popup-img").removeAttribute("title")
        document.getElementById("popup-img").removeAttribute("alt")
        popup.style.display = "none";
        popup.style.zIndex = "-1";
    };


    [].forEach.call(document.querySelectorAll('img.pic[data-src]'), function (img) {
        img.setAttribute('src', img.getAttribute('data-src'));
        img.onload = function () {
            img.removeAttribute('data-src');
        };
    });

    draw()
}
function draw() {
goChart(chartData_cn);
}</script>
</head>

<body onload="load()">
<div id="wrapper" class="lang-cn">
    <div id="lang">
        <ul>
            <li>
                <a href="#cn" id="lang-cn" title="简体中文">cn</a>
            </li>
            <li>
                <a href="#en" id="lang-en" title="English">en</a>
            </li>
        </ul>
    </div>
    <div class='heading'>
<h1>关于HTMLReport的实际项目应用</h1>
<table>
<tr><td style="width: 100%; vertical-align: top;">
  <p class='attribute'>
    <strong>
      <span class="lang-cn">启动时间:</span>
      <span class="lang-en">Start Time:</span>
    </strong> 2023-03-07 17:15:55
  </p>
  <p class='attribute'>
    <strong>
      <span class="lang-cn">结束时间:</span>
      <span class="lang-en">End Time:</span>
    </strong> 2023-03-07 17:16:09
  </p>
  <p class='attribute'>
    <strong>
      <span class="lang-cn">运行时长:</span>
      <span class="lang-en">Duration:</span>
    </strong> 0:00:13.857089
  </p>
  <p class='attribute'>
    <strong>
      <span class="lang-cn">结果:</span>
      <span class="lang-en">Status:</span>
    </strong>
    <span class="lang-cn">合计:</span>
    <span class="lang-en">Total:</span>4&nbsp;&nbsp;&nbsp;&nbsp;
    <span class="lang-cn">通过:</span>
    <span class="lang-en">Passed:</span>1&nbsp;&nbsp;&nbsp;&nbsp;
    <span class="lang-cn">失败:</span>
    <span class="lang-en">Failed:</span>0&nbsp;&nbsp;&nbsp;&nbsp;
    <span class="lang-cn">错误:</span>
    <span class="lang-en">Error:</span>3&nbsp;&nbsp;&nbsp;&nbsp;
    <span class="lang-cn">跳过:</span>
    <span class="lang-en">Skipped:</span>0&nbsp;&nbsp;&nbsp;&nbsp;
  </p>
  <p class='description'>关于HTMLReport的实际项目应用</p>
  </td>
  <td>
    <div height="400" width="600">
      <canvas id="chart" style="border: 1px solid #A4E2F9;"> 你的浏览器不支持HTML5 canvas </canvas>
    </div>
  </td>
</tr>
</table>
</div> <a href='2023_03_07 17_15_55.log'>
    <span class="lang-cn">下载日志文件</span>
    <span class="lang-en">Download log file</span>
</a>
 <p id='show_detail_line'>筛选
    <a href='javascript:showCase(0)'>
        <span class="lang-cn">摘要</span>
        <span class="lang-en">Summary</span>
    </a>
    <a href='javascript:showCase(1)'>
        <span class="lang-cn">通过</span>
        <span class="lang-en">Pass</span>
    </a>
    <a href='javascript:showCase(2)'>
        <span class="lang-cn">失败</span>
        <span class="lang-en">FAIL</span>
    </a>
    <a href='javascript:showCase(5)'>
        <span class="lang-cn">异常</span>
        <span class="lang-en">Error</span>
    </a>
    <a href='javascript:showCase(3)'>
        <span class="lang-cn">跳过</span>
        <span class="lang-en">Skip</span>
    </a>
    <a href='javascript:showCase(4)'>
        <span class="lang-cn">全部</span>
        <span class="lang-en">All</span>
    </a>
</p>
<table id='result_table'>
    <tr id='header_row'>
        <th>
            <span class="lang-cn">序号</span>
            <span class="lang-en">NO</span>
        </th>
        <th>
            <span class="lang-cn">测试组/测试用例</span>
            <span class="lang-en">Test Group/Test case</span>
        </th>
        <th>
            <span class="lang-cn">计数</span>
            <span class="lang-en">Count</span>
        </th>
        <th>
            <span class="lang-cn">通过</span>
            <span class="lang-en">Passed</span>
        </th>
        <th>
            <span class="lang-cn">失败</span>
            <span class="lang-en">Failed</span>
        </th>
        <th>
            <span class="lang-cn">错误</span>
            <span class="lang-en">Erroneous</span>
        </th>
        <th>
            <span class="lang-cn">跳过</span>
            <span class="lang-en">Skipped</span>
        </th>
        <th>
            <span class="lang-cn">统计</span>
            <span class="lang-en">Statistics</span>
        </th>
        <th>
            <span class="lang-cn">重试</span>
            <span class="lang-en">Tries</span>
        </th>
        <th>
            <span class="lang-cn">查看</span>
            <span class="lang-en">View</span>
        </th>
    </tr>
    <tr class='errorClass'>
    <td>c1</td>
    <td>test_baidu.TestCase</td>
    <td>4</td>
    <td class="passCase">1</td>
    <td class="failCase">0</td>
    <td class="errorCase">3</td>
    <td class="skipCase">0</td>
    <td style="text-align:right;">25.00%</td>
    <td>0</td>
    <td>
        <a href="javascript:showClassDetail('c1',4)">
            <span class="lang-cn">细节</span>
            <span class="lang-en">Detail</span>
        </a>
    </td>
</tr>
<tr id='et1.1'>
<td>et1.1</td>
    <td class='errorCase' colspan='7'>
        <div class='testcase'>test_back_refresh</div>
    </td>
    <td class='errorCase'>
        <div class='testcase' style="margin-left: auto;">0</div>
    </td>
    <td class='errorCase' align='center'>
        <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_et1.1.1',1, false)">
            <span class="lang-cn">错误</span>
            <span class="lang-en">error</span>
        </a>
    </td>
</tr>
<tr id='div_S_et1.1.1' name='div_et1.1.1' class="hiddenRow">
    <td colspan='10'>
        <div class="popup_window">
            <div style='text-align: right; color:red;cursor:pointer'
                 onclick="document.getElementById('div_S_et1.1.1').className = 'hiddenRow'">
                <a onfocus='this.blur();'>[x]</a>
            </div>
            <pre>et1.1.1:
2023-03-07 17:15:58,071   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:15:58,183   23748     INFO result.py(73) - 开始测试: test_back_refresh (test_baidu.TestCase)
2023-03-07 17:15:58,329   23748    ERROR result.py(191) - 测试产生错误: test_back_refresh (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 43, in test_back_refresh
    self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:15:58,330   23748     INFO result.py(96) - 测试结束: test_back_refresh (test_baidu.TestCase)
2023-03-07 17:15:58,330   23748     INFO result.py(97) - 耗时: 0.14696788787841797

</pre>
            <div><ul class='figure_ul'></ul></div>
        </div>
    </td>
</tr><tr id='et1.2'>
<td>et1.2</td>
    <td class='errorCase' colspan='7'>
        <div class='testcase'>test_search</div>
    </td>
    <td class='errorCase'>
        <div class='testcase' style="margin-left: auto;">0</div>
    </td>
    <td class='errorCase' align='center'>
        <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_et1.2.1',1, false)">
            <span class="lang-cn">错误</span>
            <span class="lang-en">error</span>
        </a>
    </td>
</tr>
<tr id='div_S_et1.2.1' name='div_et1.2.1' class="hiddenRow">
    <td colspan='10'>
        <div class="popup_window">
            <div style='text-align: right; color:red;cursor:pointer'
                 onclick="document.getElementById('div_S_et1.2.1').className = 'hiddenRow'">
                <a onfocus='this.blur();'>[x]</a>
            </div>
            <pre>et1.2.1:
2023-03-07 17:15:58,330   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:15:58,434   23748     INFO result.py(73) - 开始测试: test_search (test_baidu.TestCase)
2023-03-07 17:16:00,445   23748    ERROR result.py(191) - 测试产生错误: test_search (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 26, in test_search
    self.driver.find_element_by_id("kw").send_keys("helloworld")  # 输入“helloworld”
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:16:00,445   23748     INFO result.py(96) - 测试结束: test_search (test_baidu.TestCase)
2023-03-07 17:16:00,445   23748     INFO result.py(97) - 耗时: 2.0103440284729004

</pre>
            <div><ul class='figure_ul'></ul></div>
        </div>
    </td>
</tr><tr id='et1.3'>
<td>et1.3</td>
    <td class='errorCase' colspan='7'>
        <div class='testcase'>test_serach_clear</div>
    </td>
    <td class='errorCase'>
        <div class='testcase' style="margin-left: auto;">0</div>
    </td>
    <td class='errorCase' align='center'>
        <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_et1.3.1',1, false)">
            <span class="lang-cn">错误</span>
            <span class="lang-en">error</span>
        </a>
    </td>
</tr>
<tr id='div_S_et1.3.1' name='div_et1.3.1' class="hiddenRow">
    <td colspan='10'>
        <div class="popup_window">
            <div style='text-align: right; color:red;cursor:pointer'
                 onclick="document.getElementById('div_S_et1.3.1').className = 'hiddenRow'">
                <a onfocus='this.blur();'>[x]</a>
            </div>
            <pre>et1.3.1:
2023-03-07 17:16:00,445   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:16:00,561   23748     INFO result.py(73) - 开始测试: test_serach_clear (test_baidu.TestCase)
2023-03-07 17:16:00,566   23748    ERROR result.py(191) - 测试产生错误: test_serach_clear (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 49, in test_serach_clear
    self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:16:00,567   23748     INFO result.py(96) - 测试结束: test_serach_clear (test_baidu.TestCase)
2023-03-07 17:16:00,567   23748     INFO result.py(97) - 耗时: 0.0060002803802490234

</pre>
            <div><ul class='figure_ul'></ul></div>
        </div>
    </td>
</tr><tr id='pt1.4'>
<td>pt1.4</td>
    <td class='passCase' colspan='7'>
        <div class='testcase'>test_windows_size</div>
    </td>
    <td class='passCase'>
        <div class='testcase' style="margin-left: auto;">0</div>
    </td>
    <td class='passCase' align='center'>
        <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_pt1.4.1',1, false)">
            <span class="lang-cn">通过</span>
            <span class="lang-en">pass</span>
        </a>
    </td>
</tr>
<tr id='div_S_pt1.4.1' name='div_pt1.4.1' class="hiddenRow">
    <td colspan='10'>
        <div class="popup_window">
            <div style='text-align: right; color:red;cursor:pointer'
                 onclick="document.getElementById('div_S_pt1.4.1').className = 'hiddenRow'">
                <a onfocus='this.blur();'>[x]</a>
            </div>
            <pre>pt1.4.1:
2023-03-07 17:16:00,567   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:16:00,670   23748     INFO result.py(73) - 开始测试: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,441   23748     INFO result.py(172) - 测试执行通过: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,442   23748     INFO result.py(96) - 测试结束: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,442   23748     INFO result.py(97) - 耗时: 4.770826101303101

</pre>
            <div><ul class='figure_ul'></ul></div>
        </div>
    </td>
</tr>
    <tr id='total_row'>
        <td>&nbsp;</td>
        <td>
            <span class="lang-cn">合计</span>
            <span class="lang-en">Total</span>
        </td>
        <td>4</td>
        <td class="passCase">1</td>
        <td class="failCase">0</td>
        <td class="errorCase">3</td>
        <td class="skipCase">0</td>
        <td style="text-align:right;">25.00%</td>
        <td>0</td>
        <td>&nbsp;</td>
    </tr>
</table>
 <div id='ending'>&nbsp;</div>
    <div id="popup">
        <div class="bg">
            <img id="popup-img"/>
        </div>
    </div>
</div>
</body>

</html>

5.3.2 xxx.log

2023-03-07 17:15:55,952   25872     INFO test_runner.py(162) - 预计并发线程数:1
2023-03-07 17:15:58,071   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:15:58,183   23748     INFO result.py(73) - 开始测试: test_back_refresh (test_baidu.TestCase)
2023-03-07 17:15:58,329   23748    ERROR result.py(191) - 测试产生错误: test_back_refresh (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 43, in test_back_refresh
    self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:15:58,330   23748     INFO result.py(96) - 测试结束: test_back_refresh (test_baidu.TestCase)
2023-03-07 17:15:58,330   23748     INFO result.py(97) - 耗时: 0.14696788787841797
2023-03-07 17:15:58,330   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:15:58,434   23748     INFO result.py(73) - 开始测试: test_search (test_baidu.TestCase)
2023-03-07 17:16:00,445   23748    ERROR result.py(191) - 测试产生错误: test_search (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 26, in test_search
    self.driver.find_element_by_id("kw").send_keys("helloworld")  # 输入“helloworld”
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:16:00,445   23748     INFO result.py(96) - 测试结束: test_search (test_baidu.TestCase)
2023-03-07 17:16:00,445   23748     INFO result.py(97) - 耗时: 2.0103440284729004
2023-03-07 17:16:00,445   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:16:00,561   23748     INFO result.py(73) - 开始测试: test_serach_clear (test_baidu.TestCase)
2023-03-07 17:16:00,566   23748    ERROR result.py(191) - 测试产生错误: test_serach_clear (test_baidu.TestCase)
Traceback (most recent call last):
  File "F:\Automated-UITest-demo-update - htmlreport\testcase\test_baidu.py", line 49, in test_serach_clear
    self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_id
    return self.find_element(by=By.ID, value=id_)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="kw"]"}
  (Session info: chrome=110.0.5481.178)


2023-03-07 17:16:00,567   23748     INFO result.py(96) - 测试结束: test_serach_clear (test_baidu.TestCase)
2023-03-07 17:16:00,567   23748     INFO result.py(97) - 耗时: 0.0060002803802490234
2023-03-07 17:16:00,567   23748     INFO result.py(70) - 测试延迟启动:0.1s
2023-03-07 17:16:00,670   23748     INFO result.py(73) - 开始测试: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,441   23748     INFO result.py(172) - 测试执行通过: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,442   23748     INFO result.py(96) - 测试结束: test_windows_size (test_baidu.TestCase)
2023-03-07 17:16:05,442   23748     INFO result.py(97) - 耗时: 4.770826101303101
2023-03-07 17:16:09,611   25872     INFO test_runner.py(199) - 
Pass	test_windows_size (test_baidu.TestCase)

2023-03-07 17:16:09,612   25872    ERROR test_runner.py(201) - 
Error	test_back_refresh (test_baidu.TestCase)
Error	test_search (test_baidu.TestCase)
Error	test_serach_clear (test_baidu.TestCase)

2023-03-07 17:16:09,614   25872     INFO test_runner.py(219) - 
测试结束!
运行时间: 0:00:13.857089
共计执行用例数量:4
执行成功用例数量:1
执行失败用例数量:0
跳过执行用例数量:0
产生异常用例数量:3

5.3.3 xxx.xml

<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="关于HTMLReport的实际项目应用" errors="3" failures="0" tests="4" skipped="0" time="13.857089" >
    <testsuite name="test_baidu.TestCase" id="0" errors="3" skipped="0" tests="4" failures="0" time="6.934138298034668">
        <testcase name="test_back_refresh" classname="test_baidu.TestCase.test_back_refresh" time="0.14696788787841797">
            <error/>
        </testcase>
        <testcase name="test_search" classname="test_baidu.TestCase.test_search" time="2.0103440284729004">
            <error/>
        </testcase>
        <testcase name="test_serach_clear" classname="test_baidu.TestCase.test_serach_clear" time="0.0060002803802490234">
            <error/>
        </testcase>
        <testcase name="test_windows_size" classname="test_baidu.TestCase.test_windows_size" time="4.770826101303101">
        </testcase>
    </testsuite>
</testsuites>

5.4 testcase

  • 注意:这个用例只是说明测试报告的生成,没有对用例严格按照标准写,比如断言等
# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:test_baidu.py
# Function:打开百度网主页,在搜索栏输入“helloworld”

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import unittest


class TestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.driver = webdriver.Chrome()  # 打开Chrome浏览器
        cls.driver.get("http://www.baidu.com")  # 输入百度网址
        print("============验证浏览器的基本控制==========")

    @classmethod
    def tearDownClass(cls) -> None:
        cls.driver.quit()  # 关闭浏览器

    def test_search(self):
        print("1、搜索helloworld.并回车......")
        time.sleep(2)
        self.driver.find_element_by_id("kw").send_keys("helloworld")  # 输入“helloworld”
        time.sleep(2)
        self.driver.find_element_by_id("kw").send_keys(Keys.ENTER)  # 回车进行搜索
        time.sleep(2)
        self.driver.maximize_window()  # 最大化当前窗口

    def test_windows_size(self):
        print("2、浏览器窗口大小缩小为640*480......")
        time.sleep(2)
        self.driver.set_window_size(640, 480)  # 控制浏览器显示尺寸为640*480
        time.sleep(0.5)
        self.driver.maximize_window()  # 最大化当前窗口
        time.sleep(2)

    def test_back_refresh(self):
        print("3、先进行浏览器后退,再次输入csdn进行搜索")
        self.driver.back()
        self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
        time.sleep(1)
        self.driver.refresh() # 刷新

    def test_serach_clear(self):
        print("4、清空输入的内容......")
        self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
        time.sleep(2)
        self.driver.find_element_by_id("kw").clear()
        time.sleep(0.5)

    def csdn(self):
        print("5、进入csdn官网")
        self.driver.find_element_by_id("kw").send_keys("csdn")  # 输入csdn
        time.sleep(2)
        self.driver.find_element_by_id("kw").send_keys(Keys.ENTER)  # 回车进行搜索
        time.sleep(2)
        self.driver.find_element_by_xpath("//*[@id='1']/h3/a[1]").click()
        time.sleep(2)
        windows = self.driver.window_handles
        self.driver.switch_to.window(windows[-1])
        now_url = self.driver.current_url
        m_get_url = "https://www.csdn.net/"
        if now_url == m_get_url:
            print("经过判断,已经进入csdn官网!!")
        else:
            print("未进入到csdn官网,请检查代码!")


if __name__ == "__main__":
    unittest.main()



5.5 main.py

# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/3/7
# 文件名称:main.py
# 作用:框架的主入口函数

import time
from common.reportOut import report_out
from common.sendMain import send_main
import os


def acquire_report_address(reports_address):
    #这里方法略获取最新的测试报告,作为邮件的附件


def run_case():
    print("======开始执行!!!======")
    curpath = os.path.dirname(os.path.realpath(__file__))
    report_dir = os.path.join(curpath, "report/")        # 测试报告存放目录
    test_dir = os.path.join(curpath, "testcase/")        # 测试用例读取目录
    name_project = "关于HTMLReport的实际项目应用"
    report_out(test_dir, report_dir, name_project)
    time.sleep(5)
    # 这里方法略,调用邮件方法即可
    print("======执行结束!!!======")


if __name__ == '__main__':
    run_case()

6 运行结果

  • 会在report目录下生成三个文件;
    在这里插入图片描述

  • 命令行输出:
    在这里插入图片描述

  • 测试报告:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述


『《假如生活欺骗了你》俄·普希金』

假如生活欺骗了你,
不要悲伤,不要心急!
忧郁的日子里须要镇静:
相信吧,快乐的日子将会来临!
心儿永远向往着未来;
现在却常是忧郁。
一切都是瞬息,一切都将会过去;
而那过去了的,就会成为亲切的怀恋。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/395883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ARM架构下使用Docker安装Nacos

大家好&#xff0c;我是中国码农摘星人。 欢迎分享/收藏/赞/在看&#xff01; 注意&#xff1a;以下内容仅适用于 ARM 架构&#xff0c;X86 及 AMD 架构理论类似&#xff0c;只需要修改配置即可。 构建 MySQL 8.x 镜像 MySQL 5.x 版本没有 ARM 架构的镜像 FROM mysql:8.0.32 A…

Java 8 排序

今天分享 Java 8 进行排序的 10 个姿势&#xff0c;其实就是把 Java 8 中的 Lambda、Stream、方法引用等知识点串起来 传统排序 现在有一个 List 集合&#xff1a; public static List<User> LIST new ArrayList() {{add(new User("Lisa", 23));add(new Us…

三维人脸实践:基于Face3D的渲染、生成与重构

face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 该方法广泛用于基于三维人脸关键点的人脸生成、属性检测&#xff08;如位姿、深度、PNCC等&#xff09;&#xff0c;能够快速实现人脸建模与渲染。推荐…

如何判断多账号是同一个人?用图技术搞定 ID Mapping

原文出处&#xff1a;https://discuss.nebula-graph.com.cn/t/topic/11873 本文是一个基于图数据库 NebulaGraph 上的图算法、图数据库、图神经网络的 ID-Mapping 方法综述&#xff0c;除了基本方法思想的介绍之外&#xff0c;我还给大家弄了可以跑的 Playground。 基于图数据…

浏览器:浏览器指纹

一、引子 场景一、绑定用户与浏览器&#xff08;设备&#xff09;&#xff0c;比如某一个网站的账号给到用户&#xff0c;用户只能在自己的电脑的某浏览器使用。 场景二、精准推送广告。 场景三、公司做营销活动&#xff0c;防止活动奖品被程序薅羊毛。 等等场景我们有什么…

Qt配置VS的编译环境(以MSVC2015 64bit为例)

目录 一、原因 二、VS2015安装 三、配置套件&#xff08;Kits&#xff09; 一、原因 很多时候&#xff0c;由于VS版本切换&#xff0c;需要从高版本切换到低版本&#xff0c;或者从低版本升级到高版本&#xff0c;例如VS2019到VS2015&#xff0c;或者VS2010到VS2015。 以VS2…

辽宁千圣文化:抖音店铺怎么做二次优化?

抖音商品卡订单是指永华在抖音、抖音极速版&#xff0c;通过直播的方式出现短视频页面商品卡之后&#xff0c;直接成交商品详情页直接成交后的订单&#xff0c;那么跟着辽宁千圣文化小编来一起看看吧&#xff01;一.与政策有关1.什么是「商品卡订单」&#xff1f;用户通过抖音、…

FCN网络(Fully Convolutional Networks)

首个端到端的针对像素级预测的全卷积网络 原理&#xff1a;将图片进行多次卷积下采样得到chanel为21的特征层&#xff0c;再经过上采样得到和原图一样大的图片&#xff0c;最后经过softmax得到类别概率值 将全连接层全部变成卷积层&#xff1a;通常的图像分类网络最后几层是全…

六、死信队列

1、死信的概念 2、死信来源 3、死信实战 3.1 代码架构 正常队列绑定正常交换机正常队列绑定死信交换机死信队列绑定死信 3.2 消息TTL过期变成死信 生产者向 normal_exchange发送消息&#xff0c;通过路由键zhangsan路由到 normal-queue中&#xff0c;消息设置TTL属性 /**…

【自监督论文阅读笔记】What Makes for Good Views for Contrastive Learning?

Abstract 数据的多个视图之间的对比学习最近在自监督表示学习领域取得了最先进的性能。尽管取得了成功&#xff0c;但对不同视角选择的影响研究较少。在本文中&#xff0c;我们使用理论和实证分析来 更好地理解视图选择的重要性&#xff0c;并认为我们应该减少视图之间的互信息…

三分钟完成Stable Diffusion本地安装(零基础体验AI绘画)

三分钟完成Stable Diffusion本地安装前言安装步骤下载链接前言 最近AI绘画很火&#xff0c;很多无编程基础的小伙伴也想体验一下&#xff0c;所以写这篇博客来帮助小伙伴们愉快的体验一下~废话少说&#xff0c;我们直接开整&#xff01; 安装步骤 首先&#xff0c;下载本项目的…

电脑启动后显示器黑屏怎么办?排查下面4个问题,快速解决

电脑启动出现显示器黑屏是一个相当常见的问题。如果您遇到了这个问题&#xff0c;不要惊慌&#xff0c;因为它有很多可能的原因&#xff0c;可以采取一些简单的措施来解决它。在本文中&#xff0c;小编将介绍下面4种常见的电脑启动后显示器黑屏的原因&#xff0c;排查这些原因&…

合并两个有序链表(精美图示详解哦)

全文目录引言合并两个有序链表题目描述方法一&#xff1a;将第二个链表合并到第一个思路实现方法二&#xff1a;尾插到哨兵位的头节点思路实现总结引言 在前面两篇文章中&#xff0c;我们介绍了几道链表的习题&#xff1a;反转链表、链表的中间结点、链表的倒数第k个结点&…

基于单细胞多组学数据无监督构建基因调控网络

在单细胞分辨率下识别基因调控网络&#xff08;GRNs&#xff0c;gene regulatory networks&#xff09;一直是一个巨大的挑战&#xff0c;而单细胞多组学数据的出现为构建GRNs提供了机会。 来自&#xff1a;Unsupervised construction of gene regulatory network based on si…

力扣sql简单篇练习(二十四)

力扣sql简单篇练习(二十四) 1 各赛事的用户注册率 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 a 示例输入 b 示例输出 1.2 示例sql语句 SELECT contest_id,ROUND(count(*)/(SELECT count(user_id) FROM Users)*100,2) percentage FROM Register GROUP BY contes…

MQTT协议-使用CONNECT报文连接阿里云

使用网络调试助手发送CONNECT报文连接阿里云 参考&#xff1a;https://blog.csdn.net/daniaoxp/article/details/103039296 在前面文章介绍了如何组装CONNECT报文&#xff0c;以及如何计算剩余长度 CONNECT报文&#xff1a;https://blog.csdn.net/weixin_46251230/article/d…

【C语言】详解静态变量static

关键字static 在C语言中&#xff1a;static是用来修饰变量和函数的static主要作用为:1. 修饰局部变量-静态局部变量 2. 修饰全局变量-静态全局变量3. 修饰函数-静态函数在讲解静态变量之前&#xff0c;我们应该了解静态变量和其他变量的区别: 修饰局部变量 //代码1 #include &l…

OpenTelemetry 实现方案

OpenTelemetry 有很多种组合和实现方案&#xff0c;我们分别来了解一下 OpenTelemetry 在三种不同技术架构下的使用方式。 1、OpenTelemetry to 开源工具组合 作为经典的对各种遥测数据的处理架构&#xff0c;开源工具可将不同类型的数据存储在不同的平台&#xff0c;比如日志…

倒立摆建模

前言 系统由一辆具有动力的小车和安装在小车上的倒立摆组成&#xff0c;系统是不稳定&#xff0c;我们需要通过控制移动小车使得倒立摆保持平衡。 具体地&#xff0c;考虑二维情形如下图&#xff0c;控制力为水平力FFF&#xff0c;输出为角度θ\thetaθ以及小车的位置xxx。 力…

【WebRTC---序篇】(五)信令逻辑

关于信令的几个问题 信令发送的过程信令发送的时机:用户点connec按钮;选中connect按钮后,按回车键; Windows会分发给消息处理机制,而触发OnDefaultAction中调用Conduction的StartLogin; StartLogin里面会调用pcc_client(信令模块)的Connect; 如果是域名,进行域名解析,之后…