【致敬女神】HTMLReport应用之Unittest+Python+Selenium+HTMLReport项目自动化测试实战
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 技术栈
技术 | 版本及说明 |
---|---|
Python | V3.x(本文为3.7)===编程语言支撑 |
Selenium | V3.141.0 ===UI元素、控件的识别、定位,以及浏览器控制等 |
HTMLReport | 生成Html测试报告 |
Unittest | Python 自带===自动化测试框架 |
Smtplib | Python 自带===邮件服务 |
email | Python 自带===邮件服务 |
os | Python 自带===系统模块 |
PyCharm | Community 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, retrydef 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_image | true 只有失败才添加图片,成功用例添加的图片会被删除 |
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, retrydef 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 osdef 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_toelse:msg['To'] = ','.join(mail_to)msg['From'] = mail_frommsg['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, "&");s = s.replace(/</g, "<");s = s.replace(/>/g, ">");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/6ox = canvas.width / 2 + cSpace; //圆心oy = canvas.height / 2;tWidth = 60; //图例宽和高tHeight = 20;posX = cMargin;posY = cMargin; //textX = posX + tWidth + 15textY = 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 <span class="lang-cn">通过:</span><span class="lang-en">Passed:</span>1 <span class="lang-cn">失败:</span><span class="lang-en">Failed:</span>0 <span class="lang-cn">错误:</span><span class="lang-en">Error:</span>3 <span class="lang-cn">跳过:</span><span class="lang-en">Skipped:</span>0 </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_refreshself.driver.find_element_by_id("kw").send_keys("csdn") # 输入csdnFile "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_idreturn 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 executeself.error_handler.check_response(response)File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_responseraise 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_searchself.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_idreturn 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 executeself.error_handler.check_response(response)File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_responseraise 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_clearself.driver.find_element_by_id("kw").send_keys("csdn") # 输入csdnFile "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_idreturn 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 executeself.error_handler.check_response(response)File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_responseraise 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> </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> </td></tr>
</table><div id='ending'> </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_refreshself.driver.find_element_by_id("kw").send_keys("csdn") # 输入csdnFile "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_idreturn 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 executeself.error_handler.check_response(response)File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_responseraise 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_searchself.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_idreturn 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 executeself.error_handler.check_response(response)File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_responseraise 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_clearself.driver.find_element_by_id("kw").send_keys("csdn") # 输入csdnFile "D:\Python37\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 360, in find_element_by_idreturn 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 executeself.error_handler.check_response(response)File "D:\Python37\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_responseraise 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 unittestclass TestCase(unittest.TestCase):@classmethoddef setUpClass(cls) -> None:cls.driver = webdriver.Chrome() # 打开Chrome浏览器cls.driver.get("http://www.baidu.com") # 输入百度网址print("============验证浏览器的基本控制==========")@classmethoddef 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*480time.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") # 输入csdntime.sleep(1)self.driver.refresh() # 刷新def test_serach_clear(self):print("4、清空输入的内容......")self.driver.find_element_by_id("kw").send_keys("csdn") # 输入csdntime.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") # 输入csdntime.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_handlesself.driver.switch_to.window(windows[-1])now_url = self.driver.current_urlm_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 osdef 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
目录下生成三个文件;
-
命令行输出:
-
测试报告:
『《假如生活欺骗了你》俄·普希金』
假如生活欺骗了你,
不要悲伤,不要心急!
忧郁的日子里须要镇静:
相信吧,快乐的日子将会来临!
心儿永远向往着未来;
现在却常是忧郁。
一切都是瞬息,一切都将会过去;
而那过去了的,就会成为亲切的怀恋。
相关文章:

【致敬女神】HTMLReport应用之Unittest+Python+Selenium+HTMLReport项目自动化测试实战
HTMLReport应用之UnittestPythonSeleniumHTMLReport项目自动化测试实战1 测试框架结构2 技术栈3 实现思路3.1 使用HtmlTestRunner3.2 使用HTMLReport4 TestRunner参数说明4.1 源码4.2 参数说明5 框架代码5.1 common/reportOut.py5.2 common/sendMain.py5.3 report5.3.1 xxx.htm…...

JAVA的16 个实用代码优化小技巧
一、类成员与方法的可见性最小化 举例:如果是一个private的方法,想删除就删除。 如果一个public的service方法,或者一个public的成员变量,删除一下,不得思考很多。 二、使用位移操作替代乘除法 计算机是使用二进制…...

并发编程的三大挑战之原子性及其解决方案
目录 一、原子性问题 1、带来原子性问题的原因 2、如何解决线程切换带来的原子问题 2.1、使用synchronized关键字来保证 2.2、使用CAS来保证原子性 2.3、使用lock锁来保证 一、原子性问题 1、带来原子性问题的原因 线程切换是带来原子的根本原因,java的并发程…...
QML动画(其他的动画)
PauseAnimation (暂停动画) 为动画提供暂停 Rectangle{id:rect1width: 100;height: 100;x:100;y:100color: "lightBlue"SequentialAnimation{running: trueColorAnimation {target: rect1;property: "color";…...

Spark 配置项
Spark 配置项硬件资源类CPU内存堆外内User Memory/Spark 可用内存Execution/Storage Memory磁盘ShuffleSpark SQLJoin 策略调整自动分区合并自动倾斜处理配置项分为 3 类: 硬件资源类 : 与 CPU、内存、磁盘有关的配置项Shuffle 类 : Shuffle 计算过程的配置项Spark SQL : Spar…...
掌握Vue3模板语法,助你轻松实现高效Web开发
Vue3作为前端开发中的一种主流框架,为我们提供了多种灵活的方式来处理模板语法。除了基础的模板语法,Vue3还提供了一些高级的语法,可以让我们更好地处理组件、响应式数据和UI逻辑等。在这篇博客中,我们将介绍Vue3中的一些高级模板…...

Jmeter+Ant+Jenkins接口自动化测试平台搭建
平台简介一个完整的接口自动化测试平台需要支持接口的自动执行,自动生成测试报告,以及持续集成。Jmeter支持接口的测试,Ant支持自动构建,而Jenkins支持持续集成,所以三者组合在一起可以构成一个功能完善的接口自动化测…...
ncnn部署(CMakelists.txt)
1. NCNN 环境安装 参考博客: 基于ncnn的yolov5模型部署 1. 1 protobuf编译 打开VS2013/VS2019的X64命令行(注意不是cmd),我这里以V32013环境进行编译 > cd <protobuf-root-dir> > mkdir build-vs2013 > cd build-vs2013 > cmake -G"NMake Makefil…...

SQL分库分表
什么是分库分表? 分库分表是两种操作,一种是分库,一种是分表。 分库分表又分为垂直拆分和水平拆分两种。 (1)分库:将原来存放在单个数据库中的数据,拆分到多个数据库中存放。 (2&…...

大数据分析案例-基于逻辑回归算法构建微博评论情感分类模型
🤵♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 喜欢大数据分析项目的小伙伴,希望可以多多支持该系列的其他文章 大数据分析案例合集…...
0105深度优先搜索算法非递归2种实现对比-无向图-数据结构和算法(Java)
1 两种非递归实现 在前面我们解决无向图的单点通性和单点路径问题时,都用到了深度优先搜索算法。深度优先搜索算法可以用递归和非递归两种方式。这里讨论非递归实现。 无向图结构使用邻接表实现。 第一种非递归方法(推荐),代码如…...

传统手工数据采集耗时耗力?Smartbi数据填报实现数据收集分析自动化
企业在日常经营管理过程中,往往需要收集很多内外部的信息,清洗整理后再进行存储、分析、呈现、决策支持等各种作业,如何高效收集结构化数据是企业管理者经常要面对的问题。传统手工的数据采集方式不仅耗费了大量人力时间成本,还容…...
《Spring源码深度分析》第5章 Bean的加载
目录标题前言一、Bean加载入口与源码分析1、Bean加载的入口2、Bean加载源码二、FactoryBean的使用三、缓存中获取单例bean(待补充)前言 经过前面的分析,我们终于结束了对XML 配置文件的解析,接下来将会面临更大的挑战,就是对 bean 加载的探索…...
华为OD机试真题Java实现【求最大数字】真题+解题思路+代码(20222023)
求最大数字 题目 给定一个由纯数字组成以字符串表示的数值,现要求字符串中的每个数字最多只能出现2次,超过的需要进行删除;删除某个重复的数字后,其它数字相对位置保持不变。 如34533,数字3重复超过2次,需要删除其中一个3,删除第一个3后获得最大数值4533 请返回经过删…...

Java——异常机制
前言 随着对java的不断深入学习,在对语法以及编程思想有了一定的了解之后,在编程的过程中有可能会因为用户的输入不正确或者逻辑错误而出现异常或者错误,因此如何去捕捉与避免不应该出现的异常或者错误就变得十分重要。本文就介绍了java的异…...

【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate)12.2实时异构同步Oracle数据部署方案(下)
系列文章目录 【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate)12.2实时异构同步Oracle数据部署方案(上) 【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate)12.2实时异构同步Oracle数据部署方案(中) 【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate…...

ESP32设备驱动-土壤湿度传感器驱动
土壤湿度传感器驱动 1、土壤湿度传感器介绍 土壤湿度传感器由两个探头组成,用于测量水的体积含量。 两个探头让电流通过土壤,然后得到电阻值来测量水分值。 当有更多的水时,土壤会传导更多的电,这意味着电阻会更小。 因此,水分含量会更高。 干燥的土壤导电性差,所以当…...

公网远程连接MongoDB数据库【内网穿透】
文章目录1. 安装数据库2. 内网穿透2.1 创建隧道映射2.2 测试随机公网地址远程连接3. 配置固定TCP端口地址3.1 保留一个固定的公网TCP端口地址3.2 配置固定公网TCP端口地址3.3 测试固定地址公网远程访问MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供…...

SQL注入——floor报错注入
目录 一,涉及到的函数 rand() floor() concat_ws() as别名,group by分组 count() 报错原理 一,涉及到的函数 rand()函数:随机返回0~1间的小数 floor()函数:小数向…...
P6入门:在EPS下创建项目(P6Professional)
引言 在 Primavera P6 中,一旦创建了企业项目结构EPS,就可以开始向该结构添加项目。项目是一组活动和数据,它们构成了创建产品或服务的计划。项目有开始日期和结束日期,可以包括活动、资源、工作分解结构、组织分解结构、日历、关…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...

Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...