目录
- 前文啰嗦
- 1、框架基础介绍
- 2、编写接口代码
- 1、百听不厌的登录接口编写
- 2、基本请求接口
- 3、MD5加密方法后的登录接口脚本:
- 4、返回的`token`的处理
- 5、处理测试用例数据
- 6、pytest自动化框架脚本
- 7、allure报告
- 4、优化代码
- 1、环境初始化
- 2、定制化执行
- 3、allure报告定制化
- 4、封装邮件
- 5、封装日志
前文啰嗦
接口自动化介入时间确定:
答:越早越好,因为测试左移可以为测试侧腾出更充分的时间执行测试,效率提升的同时,以避免了日后项目更改或者delay而压缩测试的时间,从而忙的焦头烂额,还容易出现各种问题。
编写:只有api定义好了,就可以写代码
调试:模块开发完毕就可以调试,未开发完毕就可以采用mock模拟测试:
项目业务流程的自动化测试持续集成:pycharm+git+jenkins项目持续集成和自动化框架构建(CentOs7.9)
不再做过多赘述
通过python执行接口自动化总共分为三大步:
第一步:准备框架
第二步:写代码
第三步:优化
正文:
1、框架基础介绍
- 框架结构:
python+excel+pytest+allure
- 要写python代码需要的基本工具:
python
pycharm
下载地址:
- python官网(最新3.12):https://www.python.org/
- pycharm官网(社区版本即可):https://www.jetbrains.com.cn/pycharm/download/#section=windows
- fiddler:自行下载
- 搭建环境:
- python安装:python安装教程超详细
- 接口数据来源:
接口文档
fiddler自己抓包
swagger在线接口文档 - 自动化测试框架选择:
pytest
+allure+jenkins+gitlab
+jira
(pytest框架写代码、allure工具生成报告、jenkins构建持续集成、gitlab仓库服务器类型)本文只简单介绍本地代码pytest框架+allure报告部分
- 创建项目下分类目录 6包4夹总计10个,一个项目基本够用
common
:公共的,baseApi.pylibs
:基本代码库包test_case
:测试用例包configs
:代码配置包tools
:工具库包,用于读取文件信息代码util
:常规方法包datas
:测试数据/用例文件夹logs
:测试日志文件夹reports
:测试报告文件夹docs
:项目相关文档文件夹 - 测试用例类型
xmind思维导图形式,常见手工测试比较常用
excel用例,自动化使用
yaml用例,自动化使用
json/word/数据库等本文采用excel
- pytest环境搭建
- 脚本头模板展示信息
- 打开pycharm-》文件-》设置-》编辑-》代码样式-》文件和代码模板-》python Script
录入:
# _*_ coding: UTF-8 _*_
#@File ${NAME}.py
#@Time ${DATE}{TIME}
#@Name xxxxxxxxxx
#@Email xxxx@163.com
#@Software ${PRODUCT_NAME}
- 安装pytest,在pycharm底部的终端输入
pip install pytest
pip install pytest-html
- 检查是否安装成功:
pip show pytest
- 三方库的安装整个接口自动化框架会用到的三方库:可以提前安装一下通过pip
pip install allure-pytest
pip install PyYMAL
pip install request
pip install xlwt
pip install xlrd
pip install xlutils
大概就这么多,其他临时用到可以临时安装,很方便
2、编写接口代码
列举一个项目实例操作,来自采用常见的项目框架为:基于sping boot
+vue.js
前端的前后端分离的架构。
1、百听不厌的登录接口编写
1、 首先分析一个登录接口大概的思路步骤: 编写在*libs*基础代码库包中
- 需要url->发送请求,请求方法,请求体(账户、密码);
- 返回响应信息(token、status)
- 新建python文件:命名
login.py
- 数据获取:开发的接口文档或者fiddler抓包
以58同城登录为例抓包
fiddler抓包验证:
2、基本请求接口
- 正常通过手动加密密码输入的情况下,接口通了
配置HOST:
在configs
包下创建一个文件:config.py
配置内容:
HOST= 'http://192.168.xx.xx:8082'
这样就在登录和其他结果引用from configs.config import HOST
就可以啦。
但是登录密码加密了,这里就引入加密的概念:
- **1、Java常用[加密方式](http://www.jsons.cn/base64/)**
[Base64](http://www.jsons.cn/base64/)加密算法(编码方式)
[MD5](https://md5jiami.bmcx.com/)加密(消息摘要算法,验证信息完整性)
对称加密算法
非对称加密算法
数字签名算法
数字证书
- **2、应用场景**
- 1、 Base64应用场景:图片转码(应用于邮件,img标签,http加密)
- 2、 MD5应用场景:密码加密、imei加密、文件校验
- 3、非对称加密:电商订单付款、银行相关业务
登录密码采用MD5加密:
import hashlib
#MD5加密
def get_md5(psw):
indata = hashlib.md5() #实例化对象
indata.update(psw.encode('utf-8')) #加密操作
# 调用hexdigest方法,获取加密结果,hexdigest是一个十六进制的字符串值。
print(indata.hexdigest()) #默认32位小写加密数据
print((indata.hexdigest()).lower()) #输出32位小写加密数据
print((indata.hexdigest()).upper()) #输出32位大写加密数据
print((indata.hexdigest())[8:-8].lower()) #输出16位小写加密数据
print((indata.hexdigest())[8:-8].upper()) #输出16位大写加密数据
if __name__ == '__main__':
get_md5('123456')
- 登录接口自动化还需要加入MD5加密的代码
上述固定写法
3、MD5加密方法后的登录接口脚本:
:indata:登录的参数,根据用例详情可知,是json格式的键值对{"username":" xxx","psw":"xxx"},所以关联MD5加密方法处理为: indata[paw]=get_md5(indata['psw])
移到utils
公共包后,调用get_md5
方法:
4、返回的token
的处理
- 以便后续模块关联使用
:根据响应结果报文中提取到token值:return resp.json()['data']['token']
5、处理测试用例数据
- 采用excel测试用例自动化流程:
- 1、读取excel数据关键列数据(ID/path/body/response列等)
- 2、把数据关联到代码中
- 3、实际结果和预期结果对比,结果写入excel测试用例实际列
- 用例表头格式:
读写excel表语法:在tools
包下新建.py文件“getExcelData.py”
-
补充下表基础知识:
Book(class)
:由xlrd.open_work(“example.xls”)返回Sheet(class)
由Book object相关方法返回sheets
: sheet列表sheet_names
: sheet名称列表Book.sheet_by_name(sheet_name)
: 按名称提取sheetBook.sheet_by_index(sheetx)
: 按序号提取sheetnrows
: 行数ncols
: 列数cell(rowx,colx)
: 第rows行colx列的单元格(行和列定位)cell_type(rowx,colx)
: 数据类型(行和列定位)cell_value(rows,colx)
: 单元格数值(行和列定位)col(colx)
: 第colx列所有单元格组成的列表col_types(colx,start_rowx=0,end_rowx=None)
: 第colx列指定单元格数值类型组成的列表col_values(colx,start_rowx=0,end_rowx=None)
: 第colx列指定单元格数值组成的列表
读取表格库有pandas/openpyxl/xlrd
我们选择xlrd,因为它可以保留表格原始格式,方便处理
表格处理分两步:读取数据和写入数据
import xlrd # 导入读取excel模块
from xlutils.copy import copy # 导入copy模块
# 读取excel文档
def get_exceldata(sheetName, caseName): # 定义sheet名称,用例编号名称
loginlist = []
# 用例路径
excel_dir = '../datas/TestCases_V1.0.xls'
# 打开excel文档,读取数据保持表格格式:xlrd.open_workbook(path, formatting_info=True)
Book = xlrd.open_workbook(excel_dir, formatting_info=True)
# 获取指定sheet页并读取对应sheet表内容
Sheet = Book.sheet_by_name(sheetName)
# 遍历读取每个单元格需要的请求参数和预期响应数据,打包放入登录列表备用
indx = 0
for i in Sheet.col_values(0): # 第0列为case编号命名,col_value(0)第0列组成的列表
if caseName in i: # caseName在i所在的元素中(模糊匹配用例id编号Login00x)
reqdata = Sheet.cell_value(indx, 9) # sheet第9列为请求参数数据
respdata = Sheet.cell_value(indx, 11) # sheet11列为预期响应数据
#将json转为字典格式,因为用例是json格式,需要与登录接口代码保持一致
reqdata=json.loads(reqdata)
respdata=json.loads(respdata)
loginlist.append((reqdata, respdata)) # 将传参和响应打包成一个元组放入登录备用列表
indx += 1 # 遍历一行加1
# pprint.pprint(loginlist)
return loginlist #返回请求传参和响应数据,列表形式
if __name__ == '__main__':
# pprint.pprint(get_exceldata('登录模块', 'Login'))
get_exceldata('登录模块', 'Login')
写入执行结果:
要操作excel,仅仅通过xlrd库或者xlwt库无法完成,需要引入xlutils库。为保证源数据,copy一份生成新的文档进行编辑。xlrd/xlwt/xlutils关系
6、pytest自动化框架脚本
目的:将登录方法和获取用例数据关联,在test_cases
包下新建test_login.py
文件
- pytest用法规范:
- .py文件必须以
test_
或者_test
结尾 - 测试类必须也
Test
开头且不能有ini
方法 - 测试方法必须以
test
开头 - 断言使用
assert
- 运行pytest脚本:
-
pytest 用例路径 --html=./report/result.html
或 pytest.main(['当前用例路径', '--html=测试报告/xxx.html'])
- pytest的setup和teardown的用法规则
setup_moudle(class/method/function) 在整个所在该<级别>文件中的所有用例<前>执行一次
teardown_moudle(class/method/function) 在整个所在该<级别>文件中的所有用例<之后>执行一次
- 基础框架范例:
import pytest
def in(xx):
return x+1
actualresult=xxxx #实际测试结果
expectedresult=xxxx #期望结果
assert actualresult==expectedresult #对比是否一致
if_name_=='__main__':
pytest.main('执行文件名','-s','-q') #执行文件,-s打印输出信息,-q简介输出
- 上述执行基本脚本为:
import pytest
from libs.login import Login #导入登入方法
from getexceldata import get_exceldata #导入用例方法
class Test_login:
# 数据驱动: 数据样式: [({},{}),({},{})]通过数据驱动简洁实现数据匹配,indata和respdata就是get_exceldata对应的字典数据,放在方法之上,类之下的位置。
@pytest.mark.parametrize('indata, respdata', get_exceldata('登录模块', 'Login'))
def test_login(self, indata, respdata):
# 实际数据
act_resp = Login().login(indata, False)
# 断言结果对比
assert act_resp['code'] == respdata['code'] #对比的参数选择响应数据中的code,自己根据实际选择即可
if __name__ == '__main__':
# 执行文件,-s 打印信息,-q简化输出
pytest.main(['test_login.py', '-s', '-q'])
结果打印:
输出一个美化的界面报告比文本结果更好。接下来我们引入allure工具:
7、allure报告
- 1、环境搭建
- 下载allure.zip,解压到本地某个目录,
- 将解压路径allure 2.23/bin 添加到环境变量path中
- 然后再pip install allure-pytest
- 验证cmd ,输入allure展示allure使用方法即可
- 1.allure官方下载地址:
https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/ 2.选择下载版本,例如:2.9 - 3.选择压缩包下载,Windows选择.zip的文件
- 4.下载解压后进入bin目录,复制目录的路径
- 5.此电脑-系统属性-环境变量-系统变量:点击Path编辑,将复制的bin目录的路径复制到变量值里面,注意末尾以英文分号隔开,点击确定
- 7.输入allure --version
看到版本信息后代表环境变量配置成功了 - 8. 执行pip install allure-pytest
- 2、用法
- 方法1、allure报告在本地生成打开查看,allure的语法
- 需要在代码顶部导入
allure
,import allure
和os
模块,import os
if __name__ == '__main__':
# 执行文件,-s 打印信息,-q简化输出,#'--alluredir', '../reports/tmp'allure报告数据的生成路径
pytest.main(['test_login.py', '-s', '-q','--alluredir', '../reports/tmp'])
#在路径下启动allure,生成报告数据源,放在reports/tmp目录下。生成html报告到reports/html目录下
os.system('allure generate ../reports/tmp -o ../reports/html --clean')
字段 含义
allure 调用allure程序
generate 生成报告
./reports/tmp 报告的xml数据源路径
-o 参数名,后面跟html报告存储路径
./reports/html 参数值,指定html报告存储路径
--clean 清除上一次的记录
在reports/html目录下生成allure报告,然后选择打开用浏览器
展示样式:
- 方法2、启allure serve,自动在网页中打开,省去本地查找再打开
if __name__ == '__main__':
# 执行文件,-s 打印信息,-p简化输出# '--alluredir', '../reports/tmp'allure报告的生成路径
pytest.main(['test_login.py', '-s', '--alluredir', '../reports/tmp'])
#起服务allure serve:自动打开浏览器,设置默认浏览器,需要一个容器。导入os 模块
os.system('allure serve ../reports/tmp ')
.由于allure报告自动生成为比较原始,我们需要增加我们想要的信息展示以及代码的日志等需要增加,所以需要继续优化代码
4、优化代码
1、环境初始化
.@pytest.fixture()
fixture源码详细解:
fixture(scope=‘作用域’,params=None,autouse=False,ids=None,name=None)
:
scope
:fixtrue作用域:
function
: 默认范围,每个func只运行一次
class
: fixture在每个类中只运行一次
module
: fixture在每个模块中只运行一次
session
: fixture在整个测试周期只运行一次,作用于包
params
:可选参数,它将导致多个参数调用fixture功能和所有测试都使用它
autouse
:为True 为所有测试激活fixture func可以看到它,为False需要参考来激活fixture
ids
:每个字符串ID的列表,每个字符串对应于params就是测试id的一部分,没有将从params自动生成
name
:fixture的名称,默认为装饰函数的名称。
应用:作用在制定运行代码范围的首尾位置。
目的:美化python运行脚本,当执行开始时,先运行初始化语句:”开始运行自动化测试
“,当结束后,执行初始化语句:"自动化测试运行结束
"之类的提示语,更具人性化。
通过fixture的作用域
来限定作用范围来执行fixtrue脚本。
————————————————————————————
用法说明(固定写法
):
在总test_case
包下新建一个文件:必须命名
为conftest.py
,pytest框架规则限定这个命名。
import pytest
#scope指的是fixture的作用域:session<function<class<mudule<
@pytest.fixture(scope='session',autouse=True)
def start_demo(request):
print("-----开始运行自动化测试-----")
#清除数据操作
def fin():
print("-----自动化测试运行结束-----")
request.addfinalizer(fin)
一个登录模块前后各执行fixture脚本语句。使用的fixture
作用域是session
fixture
也可以手动调用
。其他文章再讲。可关注!!
2、定制化执行
目的:pytest 的mark标签化:对于pytest可以在每个模块每个类每一个方法和用例前都加上marker,当我们执行pytest时就可以根据标签指定运行。指定某个模块某个类单独执行,不用所有都要跑一遍。
注意:定制化需要注意接口的隔离,不能产生定制化执行有关联必要关系存在的脚步,隔离时也需要用到初始化
用法: .@pytest.mark.xxxx
指定了标签后,在执行时就可以指定运行
if __name__ == '__main__':
pytest.main(['test_login.py', '-s','-m','login', '--alluredir', '../reports/tmp'])
os.system('allure generate ../reports/tmp -o ../reports/html --clean')
- 问题:运行脚本出现不识别警告
.PytestUnknownMarkWarning: Unknown pytest.mark.login
- 解决:pycharm-》设置-》编辑器-》插件,需要下载这款插件解决
如果你的pycharm搜不到到官网下载:https://plugins.jetbrains.com/plugin/6981-ini/versions
- 1、查看你pycharm版本号:
- 2、搜索对应你pycharm的对应年代对应运行版本下载:
- 3、 手动导入安装你的ini插件
- 4、 安装完毕,在pycharm新建一个文档
- 使用方法
- 1、配置标签
自定义的标签会被pytest识别不了,出现警告.PytestUnknownMarkWarning: Unknown pytest.mark.xxxx
[pytest]
addopts=-vsq
testpaths=./test_case
markers =
login: run the test_login test function for tasks project
p0: test1
p1: test2
p2: test3
标记注册好后,可以通过pytest --markers
来查看
就可以正常通过标签执行指定的接口、类、方法或者某些用例了。
3、allure报告定制化
- 1、浏览器
建议使用火狐打开allure报告 - 2、allure用例描述
使用方法__________参数值___________参数说明
@allure.epic() ---------epic描述----------------敏捷里面的概念,定义史诗,往下是feature
@allure.feature()------模块名称 -------------- 功能点的描述,往下是story
@allure.story()---------用户故事--------------- 用户故事,往下是title
@allure.title()---------- 用例的标题-------------重命名html报告名称
@allure.testcase()----测试用例的链接地址–对应功能测试用例系统里面的case
@allure.issue()--------缺陷-----------------------对应缺陷管理系统里面的链接
@allure.description()-用例描述-----------------测试用例的描述
@allure.step() ---------操作步骤-----------------测试用例的步骤
@allure.severity()-----用例等级-----------------blocker,critical,normal,minor,trivial
@allure.link()-----------链接-----------------------定义一个链接,在测试报告展现
@allure.attachment()-附件-----------------------报告添加附件
标题编辑:@allure.epic()、@allure.feature()、@allure.story()、@allure.title()
.
报告展示更有层次感:
超链接编辑:jira平台、用例管理平台建立超链接地址:@allure.testcase()、@allure.issue()
报告展示:
用例描述、等级详情信息:@allure.description()、@allure.severity()
定义测试用例级别:@allure.severity()
环境信息配置
在report/tmp
文件夹下创建一个文件:environment.properties
配置信息:自己填写相关软件版本信息
Browser=Firfox
Browser.Version=77
Stand=test
Apiurl=127.0.0.1/login
python.Version=3.9.2
allure.version=2.13.3
4、封装邮件
- python对SMTP的支持
SMTP(Simple Mail Transfer Protocol)是简单传输协议,它是一组用于用于由源地址到目的地址的邮件传输规则。python中对SMTP进行了简单的封装,可以发送纯文本邮件、HTML邮件以及带附件的邮件。
python对SMTP的支持:
①email模块:负责构建邮件
②smtplib模块:负责发送邮件
③正文:html格式和表格格式
④附件:txt文本/图片
方法:在common
包下面创建一个python文件:send_email.py
固定模板,根据需要修改:
# _*_ coding: UTF-8 _*_
# @File send_email.py
# @Time 2023/3/19{TIME}
# @Name xxxxxxxx
# @Email xxxxxx@163.com
# @Software PyCharm
import smtplib # 导入发送邮件模块
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
import pandas as pd
# -----设置服务器所需信息------
mail_host = 'smtp.163.com' # 163邮箱服务器地址
port = 25 # 非ssl协议端口号
sender = # 发件箱地址
mail_pass = 'XDYKNOZZNELMZFKT' # 密码(部分邮箱为授权码)
receivers # 邮件接受方邮箱地址,注意需要[]包裹,这意味着你可以写多个邮件地址群发[]
# -----设置email信息-----
message = MIMEMultipart() # 添加一个MIMEmultipart类,处理正文及附件
message['Subject'] = '[xxx项目]登录接口[测试报告]' # 邮件主题
message['From'] = sender # 发件人
message['To'] = ";".join(receivers) # 收件人
message['CC'] = ";".join(cc) # 抄送对象
# -----html邮件模板正文附件地址文件手动上传放好-----
allurefile = '../datas/index.html'
# 推荐使用html格式的正文内容,这样比较灵活,可以附加图片地址,调整格式等
with open(allurefile, 'r', encoding='utf-8') as f:
content = f.read()
# 设置html格式参数
part1 = MIMEText(content, 'html', 'utf-8') # 邮件内容设置
# -----添加excel格式正文,附件地址文件手动上传放好-----
pd.set_option('max_colwidth', 10000) # pandas读取表格时,设置最大的列宽。使得表格内容不换行。
data_gender = pd.read_csv('../datas/testreport.csv', sep='\t', index_col=False, encoding='GBK')
temp = data_gender.to_html(index=False)
html_text_tmp = temp.replace('class', 'cellspacing=\"0\" class') # 表格类型,0表示为单外框线,默认为双外框线。
html_text = html_text_tmp.replace('<th>', '<th Bgcolor=#92cddc>') # 设置表头填充颜色
html_text = html_text.replace('text-align: right', 'text-align: center') # 表格内容居中
part2 = MIMEText(html_text, 'html', 'utf-8')
# -----添加txt文本附件,附件地址文件手动上传放好-----
with open('../datas/abc.txt', 'r', encoding='utf-8')as h:
content2 = h.read()
part3 = MIMEText('testxxxxxxxxxlogs', 'plain', 'utf-8') # 设置txt参数
# 附件设置内容类型,方便起见,设置为二进制流
part3['Content-Type'] = 'application/octet-stream'
# 设置附件头,添加文件名
part3['Content-Disposition'] = 'attachment;filename="abc.txt"'
# -----添加照片附件,附件地址文件手动上传放好-----
with open('../datas/test2.jpg', 'rb')as fp:
picture = MIMEImage(fp.read())
# 与txt文件设置相似
picture['Content-Type'] = 'application/octet-stream'
picture['Content-Disposition'] = 'attachment;filename="test2.png"'
# -----将内容附加到邮件主体中-----
message.attach(part1) # 将html邮件模板添加到容器中'''
message.attach(part2) # 将表格添加到容器中'''
message.attach(part3) # 将txt文档添加到容器中'''
message.attach(picture) # 将图片添加到容器中'''
# -----登录并发送邮件-----
try:
smtpObj = smtplib.SMTP() # 连接到服务器
smtpObj.connect(mail_host, 25) # 登录到服务器
smtpObj.login(sender, mail_pass) # 发送
smtpObj.sendmail(sender, receivers + cc, message.as_string())
# 退出
smtpObj.quit()
print('success:邮件发送成功')
except smtplib.SMTPException as e:
print('error:邮件发送失败', e) # 打印错误
附件源分别放置在datas
文件夹下:
注意excel表格为csv格式:表格另存为:csv格式
以上封装邮件提供了两种格式邮件模板
:
- 1、excel格式样式
- 2、html样式
自由选择配置使用,使用哪一个就留下哪个,其他注销掉即可
。
html邮件模板网上比较多可以自己自定义html邮件样式:样板:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>邮件模板</title>
</head>
<body>
<div style="background: #F0F2F5; padding: 35px; font-size: 14px;">
<div style="width: 750px; margin: 0 auto; background: url('https://www.henghost.com/images/hc_email_background.png') no-repeat center; background-size: cover;">
<div style="padding: 0 15px; padding-bottom: 20px;">
<div style="height: 80px; display: flex; justify-content: space-between; position: relative;">
<div>
<a href="https://www.baidu.com/index.php?tn=monline_3_dg" target="_blank" rel="noopener"><img style="margin-top: 20px; width: 150px; height: 50px;" src="https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2022%2F0220%2F50993e6dj00r7kui1000bc000hs006fg.jpg&thumbnail=650x2147483647&quality=80&type=jpg" height="50" border="0"></a>
</div>
</div>
<div style="background: #fff; padding: 0 15px; padding-bottom: 50px;">
<div style="padding: 0 9px; display: flex; justify-content: space-between; align-items: center; padding-top: 10px; padding-bottom: 10px; border-bottom: 1px solid #DBDBDB;">
<div style="display: flex;">
<a style="color: #333; text-decoration: none;" href="https://www.henghost.com/" target="_blank" rel="noopener"><span class="nav-title" style="margin-right: 50px;">某某科技公司</span></a>
</div>
<div style="display: flex;">
</div>
</div>
<div style="color: #41bf71; border: 1px solid #41BF71; background: #E0FFEC; padding: 14px 15px; display: flex; margin-top: 20px; margin-bottom: 30px;"><img style="width: 42px !important; height: 42px !important;" src="https://www.henghost.com/images/hc_console_normal.png" width="42">
<div style="font-size: 16px; font-weight: bold; margin-bottom: 5px;">
本项目测试已经完成!<br/>
测试通过!
</div>
</div>
<div>
<div><span style="font-size: 16px; font-weight: bold; position: relative; top: -4px;">测试版本</span></div>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">1.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">2.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">3.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">4.xxxxx</a><br/>
</div>
<div style="margin-top: 30px;">
<div><span style="font-size: 16px; font-weight: bold; position: relative; top: -4px;">测试项目</span></div>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">1.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">2.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">3.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">4.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">5.xxxxx</a><br/>
<a style="border: 1px solid #DBDBDB; color: #666666; color: #666666; font-size: 14px; font-weight: normal; text-align: left; padding-left: 14px;">6.xxxxx</a><br/>
Buglist:<a style="color: #006eff; text-decoration: none;" href="https://my.henghost.com/example.php?id=105807" target="_blank" rel="noopener">jiratestlink</a>
</div>
<div style="margin-top: 30px;">
<div><span style="font-size: 16px; font-weight: bold; position: relative; top: -4px;">测试结论:</span></div>
<div style="border: 1px solid #DBDBDB; padding: 10px; padding-bottom: 20px; margin-top: 20px;">
<div style="line-height: 30px;"> 1、测试通过!! </div>
<div style="line-height: 30px;"> 2、测试通过!! </div>
<div style="line-height: 30px;"> 3、测试通过!! </div>
</div>
</div>
</div>
</div>
</div>
<table class="responsive-table" style="text-size-adjust: 100%; margin-top: 18px; border-collapse: collapse !important; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="font-size: 12px; -webkit-font-smoothing: subpixel-antialiased; text-size-adjust: 100%;" align="center" valign="top">
</td>
</tr>
<tr style="padding: 0px; margin: 0px; font-size: 0px; line-height: 0;">
<td style="font-size: 12px; -webkit-font-smoothing: subpixel-antialiased; text-size-adjust: 100%;"> </td>
</tr>
<tr>
<td style="font-size: 12px; -webkit-font-smoothing: subpixel-antialiased; text-size-adjust: 100%;" align="center" valign="top">
<p style="line-height: 20.4px; text-size-adjust: 100%; font-family: 'Microsoft YaHei'!important; padding: 0px !important; margin: 0px !important; color: #7e8890 !important;"><span class="appleLinks">邮件由质量中心测试团队发出!</span></p>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
邮件演示:
5、封装日志
用到的时候就调用,不用的话可以不加
在tools
文件夹下新建一个python文件,命名logBasic.py
,固定模板写法
- 1、封装日志模块
import logging
import time
'''
1、日志存放路径
2、日志文件名
3、日志内容格式:fomat
2023_3_18 21:41:30 - logBasic.py [代码错误的行号] 级别:具体信息
执行时间 文件名 [代码错误的行号] 级别:msg(具体信息)
'''
def logger():
now_time = time.strftime("%Y_%m_%d %H.%M.%S") # 按时间生成日志文件
logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
datefmt="%Y-%m-%d %H:%M:%S %p",
filename=f'../logs/{now_time}.txt',
level=logging.DEBUG,
filemode='a')
return logging
if __name__ == '__main__':
log = logger()
log.debug('---输出用例执行日志---')
- 2、调用
在执行代码文件中导入封装类: