接口自动化测试
一、csv文件的运用
CSV 文件是用来“分离测试数据”和“测试代码”的,让自动化测试更灵活、易维护、可复用
1. 创建csv文件 (举例:登录)
位置: Autopractice\data\login_data.csv
- csv文件使用wps打开,如图:

- csv文件在pycharm中打开,如图:

2. 加载 CSV 文件到内存
from conf.setting import DIR_path
# 存储 CSV 数据
csv_data = {}
# 当前索引
current_index = 0
@classmethod
def load_csv(cls, filename):
"""加载 CSV 文件到内存"""
path = os.path.join(DIR_path, 'data', filename)
with open(path, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
# 假设第一行是字段名:username,password
headers = next(reader) # 跳过表头
cls.csv_data[filename] = [dict(zip(headers, row)) for row in reader]
logs.info(f"已加载 {len(cls.csv_data[filename])} 条数据 from {filename}")
DIR_path 内容 - - - - 获取根目录
DIR_path = os.path.dirname(os.path.dirname(__file__))
3. 获取csv文件的字段值
@classmethod
def get_data(cls, key, filename='login_data.csv'):
"""获取当前行的某个字段值"""
if filename not in cls.csv_data:
cls.load_csv(filename)
if not cls.csv_data[filename]:
logs.error(f"{filename} 中无数据")
return ""
# 返回当前行的 key 字段
current_row = cls.csv_data[filename][cls.current_index]
return current_row.get(key, "")
4. login.yaml文件修改
test_case:
- case_name: 用户名和密码正确登录验证
json:
username: "{{get_data(username, login_data.csv)}}" # 注意:传了 filename
password: "{{get_data(password, login_data.csv)}}"
5. csv替换yaml
def replace_load(self, data):
"""替换 {{func(arg1, arg2)}} 格式的字符串"""
try:
if isinstance(data, str):
str_data = data
else:
str_data = json.dumps(data, ensure_ascii=False, default=str)
logs.debug(f"replace_load 输入:{data} -> 字符串:{str_data}")
start = 0
while True:
s = str_data.find('{{', start)
if s == -1: break
b = str_data.find('(', s)
if b == -1: break
e = str_data.find(')', b)
if e == -1: break
func_name = str_data[s + 2:b].strip()
func_params = str_data[b + 1:e].strip() # 可能是 "username, login.csv"
func_all = str_data[s:e + 3].strip()
# 解析多个参数(用逗号分隔)
params = [p.strip().strip('"\'') for p in func_params.split(',')] if func_params else []
try:
func = getattr(DebugTalk(), func_name)
# 根据参数个数调用
if len(params) == 1:
extracted_value = func(params[0])
elif len(params) == 2:
extracted_value = func(params[0], params[1])
else:
extracted_value = func(*params)
replace_value = str(extracted_value) if extracted_value is not None else ""
str_data = str_data.replace(func_all, replace_value)
logs.info(f"替换:{func_all} -> {replace_value}")
start = s + len(replace_value)
except AttributeError:
logs.error(f"函数不存在:{func_name}")
start = e
continue
except Exception as e:
logs.error(f'替换失败 {func_all}: {e}')
start = e
continue
# 最后转回原始类型
if isinstance(data, dict) or isinstance(data, list):
import ast
try:
return ast.literal_eval(str_data)
except:
return str_data
return str_data
except Exception as e:
logs.error(f'replace_load 处理失败: {e}')
return data
二、钉钉推送消息
- 钉钉收到的消息样式如下

1. 设置钉钉
- 选择要推送消息的钉钉群,打开群设置,选择‘机器人’

选择添加机器人 — 添加自定义机器人


自定义机器人名字,使用 “加签” (常用)生产一个密钥

添加完成后,会生成一个Webhook地址和一个加签;通过调用Webhook接口即可向钉钉发送消息
2. 书写代码

import base64
import hashlib
import hmac
import time
import urllib.parse
import requests
def generate_sign():
"""
签名计算
:return:
"""
timestamp = str(round(time.time() * 1000))
# 钉钉机器人里面生成的秘钥
secret='SEcb163daa45904540212492d8ad7bf7c3ce428fae5211c2e94b1f0926be0778191'
secret_enc = secret.encode('utf-8')
string_to_sign ='{}\n{}'.format(timestamp,secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
return timestamp, sign
def send_dingding_msg(content,at_all = True):
"""
像钉钉群机器人推送结果
:param content: 发送的内容
:param at_all: @钉钉群里的全部人,默认为True
:return:
"""
timestamp_sign = generate_sign()
# 首先需要拿到钉钉机器人的webhook地址 + timestamp + sign
url = f'https://oapi.dingtalk.com/robot/send?access_token=SEcb163daa45904540212492d8ad7bf7c3ce428fae5211c2e94b1f0926be0778191={timestamp_sign[0]}&sign={timestamp_sign[1]}'
headers = {'Content-Type': 'application/json; charset=utf-8'}
req_data = {
'msgtype': 'text',
'text': {
'content': content
},
'at':{
'isAtAll':at_all
}
}
res = requests.post(url=url, headers=headers, data=req_data)
return res.text
if __name__ == '__main__':
content = """
各位好,本次电商项目的测试报告执行结果如下:
测试用例总共:110
通过:100
失败:7
跳过:4
异常:6
点击查看测试报告:www.baidu.com
"""
send_dingding_msg(content)
二、动态收集pytest结果进行钉钉推送
使用pytest内置的钩子函数:pytest_terminal_summary(固定写法)
根目录:

def pytest_terminal_summary(terminalreporter,exitstatus,config):
"""
每次pytest测试完成后,会自动化收集测试结果的数据
:param terminalreporter:内部终端报告对象,对象的stats属性
:param exitstatus: 将报告回操作系统的退出状态
:param config: pytest配置对象
:return:
"""
# 收集测试用例总数
case_total = terminalreporter._numcollected
# 收集测试用例通过数
passed = len(terminalreporter.stats.get("passed",[]))
# 收集测试用例失败数
failed = len(terminalreporter.stats.get("failed",[]))
# 收集测试用例错误数量
error = len(terminalreporter.stats.get("error",[]))
# 收集测试用例跳过执行数
skipped = len(terminalreporter.stats.get("skipped",[]))
# 收集测试用例执行时长
duration = time.time() - terminalreporter._sessionstarttime
content = f"""
各位好,本次电商项目的测试报告执行结果如下,请注意失败及错误的接口:
测试用例总数:{case_total}个
通过数:{passed}个
失败数:{failed}个
跳过执行数:{skipped}个
错误异常:{error}个
测试用例执行时长:{duration}
点击查看测试报告:www.baidu.com
"""
send_dingding_msg(content=content)
运行主函数,以下为运行结果

(注:以下内容为此处的测试报告链接获取)
三、pytest框架 ---- 操作Jenkins持续集成并读取报告数据
该项目必须在Jenkins中持续集成
1. 配置文件里写入Jenkins的账号密码

jenkins:
url: http://127.0.0.1:8080
username: test
password: qwe123
timeout: 30
job_name: zhwl
2. 创建快捷方法读取配置文件

def get_section_jenkins(self, option):
"""快捷方法:获取jenkins配置"""
return self.get('jenkins', option)
3. 提取测试报告链接

import json
import re
import jenkins
#首先需要安装:pip install python-jenkins
from conf.operationConfig import OperationYaml
conf = OperationYaml()
class OperJenkins:
"""读取 Jenkins 持续集成的测试报告"""
def __init__(self):
self.__config = {
'url': conf.get_section_jenkins('url'),
'username': conf.get_section_jenkins('username'),
'password': conf.get_section_jenkins("password"),
'timeout': int(conf.get_section_jenkins('timeout'))
}
self.__server = jenkins.Jenkins(**self.__config)
# 获取到Jenkins上的项目名
self.job_name = conf.get_section_jenkins('job_name')
def report_success_or_fail(self):
"""统计测试报告用例成功数、失败数、跳过数以及成功率、失败率"""
report_info = self.get_build_report()
# 提取测试报告链接
console_log = self.get_console_log()
report_line = re.search(r'http://192.168.****/job/jjapi/(.*?)allure', console_log).group(0)
return report_line
注:若有其他需求可补充内容
def get_job_number(self):
""""读取jenkins job构建号"""
build_number = self.__server.get_job_info(self.job_name).get('lastBuild').get('number')
return build_number
def get_build_job_status(self):
"""读取构建完成的状态"""
build_num = self.get_job_number()
job_status = self.__server.get_build_info(self.job_name, build_num).get('result')
return job_status
def get_console_log(self):
"""获取控制台日志"""
console_log = self.__server.get_build_console_output(self.job_name, self.get_job_number())
return console_log
def get_job_descriptian(self):
"""返回job描述信息"""
description = self.__server.get_job_info(self.job_name).get('description')
url = self.__server.get_job_info(self.job_name).get('url')
return description,url
def get_build_report(self):
"""返回第n次构建的测试报告"""
report = self.__server.get_build_test_report(self.job_name, self.get_job_number())
return report
4. 钉钉推送

def pytest_terminal_summary(terminalreporter,exitstatus,config):
"""
每次pytest测试完成后,会自动化收集测试结果的数据
:param terminalreporter:内部终端报告对象,对象的stats属性
:param exitstatus: 将报告回操作系统的退出状态
:param config: pytest配置对象
:return:
"""
# 收集测试用例总数
case_total = terminalreporter._numcollected
# 收集测试用例通过数
passed = len(terminalreporter.stats.get("passed",[]))
# 收集测试用例失败数
failed = len(terminalreporter.stats.get("failed",[]))
# 收集测试用例错误数量
error = len(terminalreporter.stats.get("error",[]))
# 收集测试用例跳过执行数
skipped = len(terminalreporter.stats.get("skipped",[]))
# 收集测试用例执行时长
duration = time.time() - terminalreporter._sessionstarttime
# 需要部署到Jenkins持续集成中去运行
oper = OperJenkins()
report = oper.report_success_or_fail()
content = f"""
各位好,本次电商项目的测试报告执行结果如下,请注意失败及错误的接口:
测试用例总数:{case_total}个
通过数:{passed}个
失败数:{failed}个
跳过执行数:{skipped}个
错误异常:{error}个
测试用例执行时长:{duration}
点击查看测试报告:{report}
"""
send_dingding_msg(content=content)
Pytest与钉钉结合的接口测试实践

被折叠的 条评论
为什么被折叠?



