自动化测试之httprunner框架hook函数实操

本篇介绍httprunner中hook函数的使用,以及通过编程能力实现建设自动化测试更全面的场景覆盖


前置:

互联网时代让我们更快的学习到什么是Httprunner

正文:

        经过上文了解到这个框架怎么使用之后,我们开始来探讨一下我们为什么要用hook函数?用hook函数能帮我们实现什么?能给我们带来什么收益?

举个例子:假设我们的业务系统目前是通过Kafka来实现消息的交互,A系统生产的消息B系统需要去消费处理后在生产消息给到下游系统

那么如果说我们需要为B系统编写这类流程的自动化,我们需要怎么实现呢?

一)设计

首先考虑一个问题:如果A系统是独立系统,如果我们写的自动化要依赖A系统的数据那就耦合度太高了。如果我们只考虑B系统的完整性,那么我们完全可以把A mock掉,将A生产的报文消息当作cases,通过python脚本来实现。

然后B系统消费后会操作数据库,那么我们断言是否可以也读数据库去判断是否预期

其次B会发消息来通知下游系统消费,那么我们是不是也可以写一个kafka监听消费消息,判断报文是否符合预期

二)实现

1、先写一个发送消息的函数,这个地方加一个参数是为了写多个cases

import json
from kafka import KafkaProducer

def sengMessage(tag):
    producer = KafkaProducer(bootstrap_servers='这个地方写集群名称')
    if('A' == tag):
        msg_dict = {
                "CMD": "这个地方是CMD",
                "Data": "这个地方是消息体",
                "Key": "这个是Key",
                "LogID": "681533193628954414",
                "Tag": "这个是标识",
                "TimeStamp": "这是个时间",
                "Topic": "这是个要发送的主题"
            }
        msg = json.dumps(msg_dict).encode()
        #partition这个地方写的是0表示放0这个Partition上了,如果要随机就看你有几个Partition,random一下
        producer.send("这个地方放主题topic", msg, partition=0)
    producer.close()

2、然后将这个函数放到debugtalk.py文件里面

3、编写一个cases,将此函数引用到setup_hooks

使用方式就是${函数名称(参数)},说明一下就是为啥要在setup_hooks里面使用,是因为需要在执行cases之前将消息发送之后系统消费到了才能走后面的流程(就是前置)

然后这个前置操作可以做:消息的发送、数据的处理(比如删除数据,更新数据等等)

4、编写一个操作数据库的函数

import pymysql
import yaml
from loguru import logger
from lib import utils


class MySQL(object):
    """Mysql数据库操作
    """
    db = {}

    def __init__(self, db):
        """初始化数据库对象
        Args:
            db (str): 数据库名字,代表了db配置的某个文件,数据库连接信息也在对应文件配置
        """
        file = f"{utils.get_root_path()}/conf/{db}.yml"

        with open(file, encoding="utf-8") as conf:
            self.db = yaml.load(conf, Loader=yaml.FullLoader)

    def execute(self, sql):
        """在mysql上执行一个sql语句,返回的结果为多条记录的元组,每条记录为dict,内容为字段-值字典
        Returns:
            [tuple]: 执行结果
        """
        self.db['cursorclass'] = pymysql.cursors.DictCursor

        try:
            connection = pymysql.connect(**self.db)
            logger.info(f"execute sql: [{sql}]")
            with connection.cursor() as cursor:
                cursor.execute(sql)
                data = cursor.fetchall()
                logger.debug(f"execute ret: [{data}]")
                connection.commit()
        finally:
            connection.close()
        return data
#怎么使用呢,就是直接用这个函数传配置文件名称,sql
if __name__ == '__main__':
    MySQL("auto").execute("select * from A limit 1")

设置放置存储数据库配置文件的目录

数据库配置

host: IP
port: 端口
user: 用户名
passwd: 密码
db: 数据库

5、能操作数据库之后,那么我们是否可以将查询回来的全部重要字段(什么自增主键ID、时间啥的就不用断言了)进行判断了,写一个比较json的函数

class Check(object):

    def CheckSql(prama):
        db = 'auto'
        errmsgl_success = 'success'
        sql = 'SELECT A,B,C,D,E,F,G,H,I,J,K from AAA where aa=' + '\'' + str(
            prama) + '\''
        sql_count = 'SELECT count(1) from AAA  WHERE (aa = ' + "\'" + str(
            prama) + "\'" + ')'
        # 先查询条数,没有就重试,如果重试还没有就失败
        count = MySQL(db).execute(sql_count)[0]['count(1)']
        retry = 0
        while count == 0 and retry < 15:
            time.sleep(3)
            retry = retry + 1
            count = MySQL(db).execute(sql_count)[0]['count(1)']

        aa = MySQL(db).execute(sql)[0]

        response = json.dumps(aa)
        print(response)
        # 这个地方,假如A字段是一个JSON,这个地方是做了一个处理,让这个json排序,就不会出现每次查询这个json都是乱序得导致断言失败
        # 假设A字段:[{fee:1},{fee:2},{fee:3}],那么我们就用fee来排序
        A = eval(str(aa["A"]))
        aa["A"] = sorted(A, key=lambda x: (x['fee']), reverse=True)
        print(json.dumps(aa))
        # 这个地方用的直接是传进来得参数作为文件名称
        checkfile__detail = str(prama) 
        detail_ret = Check.checkfile_json(aa, checkfile__detail)
        if detail_ret != errmsgl_success:
            return '文件名称:' + checkfile__detail + ', 失败原因: ' + detail_ret

        return errmsgl_success
    
    # 读取对应的预期文件,这个函数可以抽出来,当一个公共方法
    def checkfile_json(requestdata, checkfile):
        # 文件地址当前项目/checkfile/checkfile.json
        basic_url = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/checkfile'
        if checkfile:
            with open(basic_url + checkfile + '.json', 'r') as f:
                setdatas = json.load(f)

        return Check.cmp(requestdata, setdatas)

    def cmp(src_data, dst_data):
        msg = 'success'

        if isinstance(dst_data, dict):
            """若为dict格式"""
            for key in dst_data:
                if key not in src_data:
                    return 'key: ' + str(key) + ' 在请求中不存在'
              
                msg = Check.cmp(src_data[key], dst_data[key])
                if msg != 'success':
                    return msg
        elif isinstance(dst_data, list):
            """若为list格式"""
            for src_list, dst_list in zip((src_data), (dst_data)):
                """递归"""
                msg = Check.cmp(src_list, dst_list)
                if msg != 'success':
                    return msg
        else:
            if str(src_data) != str(dst_data):
                return 'src_data != dst_data, src_data: ' + str(src_data) + ', dst_data: ' + str(dst_data)
        return msg

6、断言函数既然已经写好了,那就直接引用,同上面那个流程,加载到debugtaik.py文件之后,我们需要在cases断言处加上此函数,还是用${函数名称(参数)}来引用

7、最后编写一下消费kafka消息进行断言的函数

    def check_kafka_messgae(topic, bootstrap_servers, checkfile, enable_auto_commit=False, consumer_timeout_ms=300):
        # 参数分别是主题,集群,断言文件地址,自动提交,超时时间
        consumer = KafkaConsumer(bootstrap_servers=[bootstrap_servers], enable_auto_commit=enable_auto_commit,
                                 consumer_timeout_ms=consumer_timeout_ms)
        # 这个地方只监听一个partition == 0的
        topic_partition = TopicPartition(topic=topic, partition=0)
        lastoffset = consumer.end_offsets([topic_partition])[topic_partition]
        consumer.assign([topic_partition])
        consumer.seek(topic_partition, lastoffset - 1)
        messagelist = ''
        for message in consumer:
            messagelist = message.value
        messagelist = json.loads(json.loads(str(messagelist, 'utf-8'))['Data'])
        print('topic数据' + str(messagelist))
        # checkfile_json这个方法上面写得函数里面有,直接调用
        return checkfile_json(messagelist, checkfile)

8、同上,还是将这个函数加入到debugtaik.py,在validate里面进行断言判断

总结:

将上面的流程串起来之后发现,从kafak消息的发送、数据库的查询、文件读取/数据判断,到kafka消息监听断言整个流程能够完成上述的诉求。

通过一个例子来解读hook函数的作用。在真实的业务场景下,我们在编写自动化cases的时候完全可以用纯代码的方式来实现我的复杂的业务场景。通过这种方式能更有效的认可自动化去替代人工操作。


后话:

个人认为自动化的灵魂不是用什么工具,而是断言。不管是人工还是程序自动操作都需要有一个判断依据,如果这个判断依据十分有效,那么我们只需要完善场景cases就能实现自动化替换人工(百分之80吧)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL分区表(14/16)

分区表 基本概述 分区表是数据库中一种用于优化大型表数据管理和查询性能的技术。它将一个表的数据根据特定的规则或条件分割成多个部分&#xff0c;每个部分称为一个分区。每个分区可以独立于其他分区进行存储、管理和查询&#xff0c;这样可以提高数据处理的效率&#xff0…

mybatis(9)-逆向工程+PageHelper+注解方式开发

最后一篇&#xff01;&#xff01; 1、逆向工程1.1、普通版1.2、增强版 2、PageHelper2.1 limit2.2 插件 3、注解开发3.1 Insert3.2Delete3.3 Update3.4 Select Results 1、逆向工程 1.1、普通版 所谓的逆向工程是&#xff1a;根据数据库表逆向生成Java的pojo类&#xff0c;S…

智过网:注册安全工程师注册有效期与周期解析

在职业领域&#xff0c;各种专业资格认证不仅是对从业者专业能力的认可&#xff0c;也是保障行业安全、规范发展的重要手段。其中&#xff0c;注册安全工程师证书在安全生产领域具有举足轻重的地位。那么&#xff0c;注册安全工程师的注册有效期是多久呢&#xff1f;又是几年一…

伺服系统中滤波器算法的工程实现方案

此文章主要致力于描述如何将伺服驱动系统中的数字滤波器用编程语言来实现。

【动态规划 区间dp 位运算】100259. 划分数组得到最小的值之和

本文涉及知识点 动态规划 区间dp 位运算 LeetCode100259. 划分数组得到最小的值之和 给你两个数组 nums 和 andValues&#xff0c;长度分别为 n 和 m。 数组的 值 等于该数组的 最后一个 元素。 你需要将 nums 划分为 m 个 不相交的连续 子数组&#xff0c;对于第 ith 个子数…

银行渠道整合平台应用架构

渠道整合平台将 功能微服务化&#xff0c;将服务流程标准化。微服务 化的功能能够进行各种组合使用。而标准化的流程可同时作用于所有渠道&#xff0c;保证体验一致。未来在进行流程变更的时候可有效避免各渠道的重复开发。 • 渠道整合平台避免了各个渠道对于同一个业务的差异…

C# dynamic 数据类型

在C#中&#xff0c;dynamic是一种数据类型&#xff0c;它允许在运行时推迟类型检查和绑定。使用dynamic类型&#xff0c;可以编写更具灵活性的代码&#xff0c;因为它允许在编译时不指定变量的类型&#xff0c;而是在运行时根据实际情况进行解析。 dynamic类型的变量可以存储任…

你真的会处理python代码异常吗?

Python 使用称为异常(exception&#xff09;的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时&#xff0c;它都会创建一个异常对象。如果你编写了处理该异常的代码&#xff0c;程序将继续运行&#xff1b;如果你未对异常进行处理&#xff0c;程序将停…

什么是面向对象思想?

面向对象不是一种技术&#xff0c;而是一种思想。它指导我们以什么形式组织代码&#xff0c;以什么思路解决问题。 面向对象编程&#xff0c;是一种通过对象方式&#xff0c;把现实世界映射到计算机世界的编程方法。 面向对象解决问题的思路&#xff1a;把构成问题的事物分解成…

响应式导航栏不会做?看我一分钟学会制作导航栏!

引言 随着互联网技术的飞速发展&#xff0c;用户体验在网页设计中的重要性日益凸显。其中&#xff0c;导航栏作为网页的“指南针”&#xff0c;不仅能帮助用户快速定位所需内容&#xff0c;还能体现网站的整体风格和设计理念。本文将介绍如何使用HTML、CSS和JavaScript制作一个…

1.16 LeetCode总结(基本算法)动态规划2

70. 爬楼梯 首先想到的是递归&#xff1a; // 递归 int climbStairs(int n) {if (n 1) {return 1;} else if (n 2) {return 2;}return climbStairs(n - 1) climbStairs(n - 2); }我们先来看看这个递归的时间复杂度吧&#xff1a; 递归时间复杂度 解决一个子问题时间*子问…

【翻译】再见, Clean Code!

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 【翻译】再见, Clean Code!正文那是一个深夜次日早晨这只是一个阶段 【翻译】再见…

【植物大战僵尸融合机器学习】+源码

上期回顾&#xff1a; 今天给大家推荐一个Gtihub开源项目&#xff1a;PythonPlantsVsZombies&#xff0c;翻译成中就是植物大战僵尸。 《植物大战僵尸》是一款极富策略性的小游戏。可怕的僵尸即将入侵&#xff0c;每种僵尸都有不同的特点&#xff0c;例如铁桶僵尸拥有极强的抗…

【设计模式学习】单例模式和工厂模式

꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转…

Java-博客系统(前后端交互)

目录 前言 博客系统基本情况 1 创建项目&#xff0c;引入依赖 2 数据库设计 2.1 分析 2.2 建库建表 3 封装数据库 3.1 在java目录下创建DBUtil类&#xff0c;通过这个类对数据库进行封装 3.2 在java目录下创建实体类&#xff08;博客类Blog&#xff09; 3.2 在java目录下创建…

vwmare+Ubuntu20.04安装超级保姆级完整教程

强烈建议先完整的看完一遍教程在进行安装以免出现问题&#xff01;&#xff01;&#xff01; 如果遇到error&#xff1a;建议复制error后面的信息然后到浏览器搜索&#xff0c;查找解决方案&#xff0c;其次在进行某个不确定的操作时&#xff0c;建议先保存快照&#xff0c;这样…

uboot操作指令1

文章目录 前言一、信息查询命令1.bdinfo用于查看板子的信息2.printenv 打印环境变量3.version查看uboot版本 二、环境变量操作命令1.setenv修改环境变量2.setenv新建环境变量3.setenv删除环境变量 三、内存操作命令1.md 命令2.nm命令3.mm命令4.mw命令 四、网络操作命令1.ping命…

Zookeeper与kafka

目录 一、zookeeper 1.1.zookeeper概述 1.2.Zookeeper 工作机制 1.3. Zookeeper 特点 1.4.Zookeeper 数据结构 1.5.Zookeeper 应用场景 1.6.Zookeeper 选举机制 第一次启动选举机制 非第一次启动选举机制 选举Leader规则&#xff1a; 1.7.部署 Zookeeper 集群 1.7.…

AI人工智能讲师大模型培训讲师叶梓 大语言模型(LLM)在科学文献摘要领域的应用

大语言模型&#xff08;LLM&#xff09;在科学文献摘要领域的应用是一个前沿且迅速发展的技术趋势。通过结合GitHub上yobibyte的Compressor项目&#xff0c;我们可以深入探讨这一技术方案的潜力和实现方式。 技术背景 随着科学研究的快速发展&#xff0c;每天都有大量的科学文…

matlab学习(三)(4.9-4.15)

一、空域里LSB算法的原理 1.原理&#xff1a; LSB算法通过替换图像像素的最低位来嵌入信息。这些被替换的LSB序列可以是需要加入的水印信息、水印的数字摘要或者由水印生成的伪随机序列。 2.实现步骤&#xff1a; &#xff08;1&#xff09;将图像文件中的所有像素点以RGB形…