Pytest+requests进行接口自动化测试7.0(csv文件运用 + 钉钉测试结果推送)

Pytest与钉钉结合的接口测试实践

一、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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值