python测试工具: 实现数据源自动核对

news2025/1/21 4:53:16

测试业务需要:

现有A系统作为下游数据系统,上游系统有A1,A2,A3...

需要将A1,A2,A3...的数据达到某条件后(比如:A1系统销售单提交出库成功)自动触发MQ然后再经过数据清洗落到A系统,并将清洗后数据通过特定规则汇总在A系统报表中

现在需要QA同学验证的功能是:

A系统存储数据清洗后的库表(为切片表)有几十个,且前置系统较多,测试工作量也较多

需要核对清洗后A存库数据是否准确

清洗规则:(1)直接取数 (2)拼接取数 (3)映射取数

直接取数字段在2系统表中字段命名规则一致

so,以下测试工具是针对直接取数规则来开发,以便于测试

代码实现步骤:

(1)将表字段,来源系统表和切片表 数据库链接信息,查询字段 作为变量

将这些信息填入input.xlsx 作为入参

(2)读取表字段,根据来源系统表 数据库链接信息,查询字段

查询来源库表,将查询出字段值存储outfbi.xlsx

  (3)读取表字段,根据切片表 数据库链接信息,查询字段

查询切片库表,将查询出字段值存储outods.xlsx

(4)对比outfbi.xlsx,outods.xlsx的字段值

对比后生成result.xlsx文件,新增列校验结果

核对字段值一致校验结果为Success,否则为Fail

代码如下:

入参文件见附件

DbcheckApi.py
import os
import pymysql
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
import datetime
import ast

"""测试数据路径管理"""
SCRIPTS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
GENERATECASE_DIR = os.path.join(SCRIPTS_DIR, "dbcheck")
inputDATAS_DIR = os.path.join(GENERATECASE_DIR, "inputdata")
outDATAS_DIR = os.path.join(GENERATECASE_DIR, "outdata")

class DbcheckApi():

 def __init__(self,data):
    self.inputexcel=data
    workbook = load_workbook(filename=self.inputexcel)
    sheet = workbook['数据源']
    # 读取来源表-连接信息
    sourcedb_connection_info = ast.literal_eval(sheet['B3'].value)
    odsdb_connection_info = ast.literal_eval(sheet['B4'].value)
    source_db = sheet['C3'].value.strip()
    ods_db = sheet['C4'].value.strip()
    source_queryby = sheet['D3'].value.strip()
    ods_queryby = sheet['D4'].value.strip()
    print(sourcedb_connection_info)
    print(odsdb_connection_info)
    print(source_db)
    print(ods_db)
    print(source_queryby)
    print(ods_queryby)

    self.sourcedb = sourcedb_connection_info
    self.odsdb = odsdb_connection_info
    self.source_db = source_db
    self.ods_db = ods_db
    self.source_queryby = source_queryby
    self.ods_queryby = ods_queryby

 def source_select_db(self):
    host = self.sourcedb.get('host')
    port = self.sourcedb.get('port')
    user = self.sourcedb.get('user')
    passwd = self.sourcedb.get('passwd')
    db = self.sourcedb.get('db')
    if not host or not port or not user or not passwd or not db:
        error_msg = "连接信息不完整"
        return {"code": -1, "msg": error_msg, "data": ""}

    cnnfbi = pymysql.connect(
        host=host,
        port=port,
        user=user,
        passwd=passwd,
        db=db
    )

    cursor = cnnfbi.cursor()
    try:
        # 读取Excel文件
        df = pd.read_excel(self.inputexcel, sheet_name='Sheet1')
        # 获取第1列,从第2行开始读取的字段名
        fields = df.iloc[1:, 0].tolist()
        print(fields)

        # 构建查询SQL语句
        sql = "SELECT {} FROM {} WHERE {}".format(', '.join(fields), self.source_db, self.source_queryby)
        print(sql)
        # 执行查询语句
        cursor.execute(sql)
    except pymysql.err.OperationalError as e:
        error_msg = str(e)
        if "Unknown column" in error_msg:
            column_name = error_msg.split("'")[1]
            msg={"code": -1, "msg": f"列字段 {column_name} 在 "+self.source_db+" 表结构中不存在,请检查!", "data": ""}
            print(msg)
            return {"code": -1, "msg": f"列字段 {column_name} 在 "+self.source_db+" 表结构中不存在,请检查!", "data": ""}
        else:
            return {"code": -1, "msg": error_msg, "data": ""}
            print(error_msg)

    # 获取查询结果
    result = cursor.fetchall()
    # 关闭游标和连接
    cursor.close()
    cnnfbi.close()
    # 检查查询结果是否为空
    if not result:
        return {"code": -1, "msg": f"查询无数据,请检查sql: {sql}", "data": ""}
    else:
     # 将结果转换为DataFrame对象
     df = pd.DataFrame(result, columns=fields)
     odskey=self.source_db+'表-字段'
     odsvalue=self.source_db+'表-字段值'
     # 创建新的DataFrame对象,将字段和对应值放在两列
     df_new = pd.DataFrame({odskey: fields, odsvalue: df.iloc[0].values})
     outexcel = os.path.join(outDATAS_DIR,  'outputfbi.xlsx')
     # 导出结果到Excel文件
     df_new.to_excel(outexcel, index=False)


 def ods_select_db(self):
    host = self.odsdb.get('host')
    port = self.odsdb.get('port')
    user = self.odsdb.get('user')
    passwd = self.odsdb.get('passwd')
    db = self.odsdb.get('db')

    if not host or not port or not user or not passwd or not db:
        raise ValueError("连接信息不完整")
    cnnfbi = pymysql.connect(
        host=host,
        port=port,
        user=user,
        passwd=passwd,
        db=db
    )
    cursor = cnnfbi.cursor()
    try:
        # 读取Excel文件
        df = pd.read_excel(self.inputexcel, sheet_name='Sheet1')
        # 获取第1列,从第2行开始读取的字段名
        fields = df.iloc[1:, 0].tolist()
        print(fields)

        # 构建查询SQL语句
        sql = "SELECT {} FROM {} WHERE {}".format(', '.join(fields), self.ods_db, self.ods_queryby)
        print(sql)
        # 执行查询语句
        cursor.execute(sql)
    except pymysql.err.OperationalError as e:
        error_msg = str(e)
        if "Unknown column" in error_msg:
            column_name = error_msg.split("'")[1]
            return {"code": -1, "msg": f"列 {column_name} 不存在"+self.ods_db+" 表结构中,请检查!", "data": ""}
        else:
            return {"code": -1, "msg": error_msg, "data": ""}


    # 获取查询结果
    result = cursor.fetchall()
    # 关闭游标和连接
    cursor.close()
    cnnfbi.close()

    # 将结果转换为DataFrame对象
    df = pd.DataFrame(result, columns=fields)
    # 创建新的DataFrame对象,将字段和对应值放在两列
    odskey=self.ods_db+'表-字段'
    odsvalue=self.ods_db+'表-字段值'

    df_new = pd.DataFrame({odskey: fields, odsvalue: df.iloc[0].values})
    # 导出结果到Excel文件
    outexcel = os.path.join(outDATAS_DIR,  'outputfms.xlsx')

    df_new.to_excel(outexcel, index=False)


 def check_order(self):
    self.source_select_db()
    self.ods_select_db()
    outputfbi = os.path.join(outDATAS_DIR,  'outputfbi.xlsx')
    outputfms = os.path.join(outDATAS_DIR,  'outputfms.xlsx')
    df_a = pd.read_excel(outputfbi)
    df_b = pd.read_excel(outputfms)
    # 创建新的DataFrame对象用于存储C表的数据
    df_c = pd.DataFrame()
    # 将A表的列写入C表
    for col in df_a.columns:
        df_c[col] = df_a[col]
    # 将B表的列���入C表
    for col in df_b.columns:
        df_c[col] = df_b[col]

    odsvalue=self.ods_db+'表-字段值'
    fbivalue=self.source_db+'表-字段值'
    # 比对A2和B2列的值,如果不一致,则在第5列写入"校验失败"
    df_c['校验结果'] = ''
    for i in range(len(df_c)):
        if pd.notnull(df_c.at[i, fbivalue]) and pd.notnull(df_c.at[i, odsvalue]):
            fbivalue_rounded = df_c.at[i, fbivalue]
            odsvalue_rounded = df_c.at[i, odsvalue]
            if isinstance(fbivalue_rounded, (int, float)):
                fbivalue_rounded = round(fbivalue_rounded, 3)
            elif isinstance(fbivalue_rounded, datetime.datetime):
                fbivalue_rounded = round(fbivalue_rounded.timestamp(), 3)
            else:
                try:
                    fbivalue_rounded = round(float(fbivalue_rounded), 3)
                except ValueError:
                    pass
            if isinstance(odsvalue_rounded, (int, float)):
                odsvalue_rounded = round(odsvalue_rounded, 3)
            elif isinstance(odsvalue_rounded, datetime.datetime):
                odsvalue_rounded = round(odsvalue_rounded.timestamp(), 3)
            else:
                try:
                    odsvalue_rounded = round(float(odsvalue_rounded), 3)
                except ValueError:
                    pass
            if fbivalue_rounded != odsvalue_rounded:
                df_c.at[i, '校验结果'] = 'Fail'
            else:
                df_c.at[i, '校验结果'] = 'Success'
    # 将结果写入到C.xlsx文件
    df_c.to_excel('checkhead_result.xlsx', index=False)
    # 打开C.xlsx文件并设置背景色
    book = load_workbook('checkhead_result.xlsx')
    writer = pd.ExcelWriter('checkhead_result.xlsx', engine='openpyxl')
    writer.book = book

    # 获取C.xlsx的工作表
    sheet_name = 'Sheet1'
    ws = writer.book[sheet_name]
    # 设置背景色为红色
    red_fill = PatternFill(start_color='FFFF0000', end_color='FFFF0000', fill_type='solid')
    # 遍历校验结果列,将不一致的单元格设置为红色背景
    for row in ws.iter_rows(min_row=2, min_col=len(df_c.columns), max_row=len(df_c), max_col=len(df_c.columns)):
        for cell in row:
            if cell.value == 'Fail':
                cell.fill = red_fill
    # 保存Excel文件
    writer.save()
    writer.close()

if __name__ == '__main__':
    inputexcel = os.path.join(inputDATAS_DIR,  'input.xlsx')
    DbcheckApi(inputexcel).check_order()

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ElasticSearch 架构设计

介绍 ElasticSearchMySQLIndexTableDocumentRowFieldColumnMappingSchemaQuery DSLSQLaggregationsgroup by,avg,sumcardinality去重 distinctreindex数据迁移 ElasticSearch 中的一个索引由一个或多个分片组成 每个分片包含多个 segment(分…

【Spark精讲】一文讲透SparkSQL执行过程

SparkSQL执行过程 逻辑计划 逻辑计划阶段会将用户所写的 SQL语句转换成树型数据结构(逻辑算子树), SQL语句中蕴含的逻辑映射到逻辑算子树的不同节点。 顾名思义,逻辑计划阶段生成的逻辑算子树并不会直接提交执行,仅作为中间阶段 。 最终逻辑…

算法每日一题:参加考试的最大学生数 | 动态规划 | 状态压缩

大家好,我是星恒 今天的题目竟然是一道困难题目,看着就不简单,我们的目标是:理解如何做 学一些思路! 这次题目涉及的知识:动态规划,状态压缩(位运算) 给你一个 m * n 的…

挑战Python100题(8)

100+ Python challenging programming exercises 8 Question 71 Please write a program which accepts basic mathematic expression from console and print the evaluation result. 请编写一个从控制台接受基本数学表达式的程序,并打印评估结果。 Example: If the follo…

padStart(),padEnd()

今天获取当前时间的时候,gpt输出这样的代码,padStart(2, 0) function getCurrentDateFormatted() {const currentDate new Date();const month (currentDate.getMonth() 1).toString().padStart(2, 0);const day currentDate.getDate().toString().…

车载毫米波雷达及芯片新趋势研究1--毫米波雷达与其它车载传感器互补,研发及量产门槛较高

1.1 毫米波雷达是利用毫米波电磁波波束工作的雷达,车载是首要应用场景  毫米波雷达是一种以波长位于1-10mm、频率在30-300GHz的电磁波作为放射波的雷达传感器。  毫米波雷达利用毫米波波束进行工作。①检测障碍物时: 直接通过有无回波确认&#xff…

Power BI - 5分钟学习合并文件

每天5分钟,今天介绍Power BI合并文件 什么是合并文件? 合并文件就是将具有相同架构的多个文件合并到单个逻辑表中。 如果要合并同一文件夹中的所有文件时,此功能非常有用。 例如,如果你有一个文件夹,其中包含公司的所…

API 开放平台项目(已整理,已废弃)

项目大纲 前端 React 18Ant Design Pro 5.x 脚手架Ant Design & Procomponents 组件库Umi 4 前端框架OpenAPI 前端代码生成 后端 Java Spring BootMySQL 数据库MyBatis-Plus 及 MyBatis X 自动生成API 签名认证(Http 调用)Spring Boot Starter&#…

探索大型预训练模型:解析人工智能的通用知识引擎

目录 前言1 大型预训练模型的演进与重要性1.1 Word2Vec1.2 Transformer1.3 GPT模型 2 大型预训练模型的发展趋势2.1 参数规模与速度的飞跃提升2.2 数据量的持续增长2.3 知识丰富性与少样本学习的突破 3 大型预训练模型的核心机制结语 前言 在当今迅猛发展的人工智能领域&#…

推荐几个开源HTTP服务接口快速生成工具

在现在流行微服务、前后端分离软件开发架构下,基于标准RESTful/JSON的HTTP接口已经成为主流。在实际业务中有很多需要快速开发调用数据服务接口的需求,但团队中缺乏专业的后端开发人员,比如: (1)数据库表已…

【C++杂货铺】C++11新特性——lambda

文章目录 一、C98中的排序二、先来看看 lambda 表达式长什么样三、lambda表达式语法3.1 捕捉列表的使用细节 四、lambda 的底层原理五、结语 一、C98中的排序 在 C98 中,如果要对一个数据集合中的元素进行排序,可以使用 std::sort 方法,下面…

nginx源码分析-2

这一章内容讲述nginx进程的创建和工作进程是如何开展工作的。 ngx_cycle_s结构体是nginx中非常重要的结构体之一,它代表了nginx运行时的环境。在nginx的生命周期中用于保存各种与运行时状态相关的信息。 在ngx_start_worker_processes中会根据配置的工作进程数量&a…

集群部署篇--Redis 哨兵模式

文章目录 前言一、哨兵模式介绍:1.1 介绍:1.2 工作机制: 二、哨兵模式搭建:2. 1 redis 主从搭建:2.2 setinel 集群搭建:2.2.1 配置: sentinel.conf :2.2.2 运行容器:2.2.…

4.25 构建onnx结构模型-Unsuqeeze

前言 构建onnx方式通常有两种: 1、通过代码转换成onnx结构,比如pytorch —> onnx 2、通过onnx 自定义结点,图,生成onnx结构 本文主要是简单学习和使用两种不同onnx结构, 下面以 Unsuqeeze 结点进行分析 方式 方法…

python+django大自然环境保护宣传网站62r9b

本课题使用Python语言进行开发。基于web,代码层面的操作主要在PyCharm中进行,将系统所使用到的表以及数据存储到MySQL数据库中 本系统由后台管理子系统,登录子系统,按登陆角色及权限划分为管理员:个人中心,用户管理,文…

UI演示双视图立体匹配与重建

相关文章: PyQt5和Qt designer的详细安装教程:https://blog.csdn.net/qq_43811536/article/details/135185233?spm1001.2014.3001.5501Qt designer界面和所有组件功能的详细介绍:https://blog.csdn.net/qq_43811536/article/details/1351868…

【机组期末速成】CPU的结构与功能|CPU结构|指令周期概述|指令流水线|中断系统

🎥 个人主页:深鱼~🔥收录专栏:计算机组成原理🌄欢迎 👍点赞✍评论⭐收藏 前言: 最近在备战期末考试,所以本专栏主要是为了备战期末计算机组成原理这门考试,讲的比较浅显&…

CSS 丝带形状效果

CSS 丝带形状效果如图: 通过CSS创建折叠丝带形状 这里代码应该比较清晰易懂,clip-path 的值应该也容易理解。要注意的是,我们使用了 color-mix() 函数,这个属性允许创建主颜色的深色版本。现在如果我们将元素旋转相反的方向&#…

Element|InfiniteScroll 无限滚动组件的具体使用方法

目录 InfiniteScroll 无限滚动 基本用法 详细说明 v-infinite-scroll 指令 infinite-scroll-disabled 属性 infinite-scroll-distance 属性 总结 需求背景 : 项目统计管理列表页面,数据量过多时在 IE 浏览器上面会加载异常缓慢,导致刚…

Java多线程<二>多线程经典场景

leetcode 多线程刷题 上锁上一次,还是上多次? 同步的顺序。 1. 交替打印字符 使用sychronize同步锁使用lock锁使用concurrent的默认机制使用volitale关键字 Thread.sleep() / Thread.yield机制使用automic原子类 方式1 :使用互斥访问st…