2024一定要看的文章系列!!!接口自动化测试框架思路和实战(5):【推荐】混合测试自动化框架(关键字+数据驱动)

news2024/11/15 17:30:37

混合测试自动化框架(关键字+数据驱动)

关键字驱动或表驱动的测试框架

  这个框架需要开发数据表和关键字。这些数据表和关键字独立于执行它们的测试自动化工具,并可以用来“驱动"待测应用程序和数据的测试脚本代码,关键字驱动测试看上去与手工测试用例很类似。在一个关键字驱动测试中,把待测应用程序的功能和每个测试的执行步骤一起写到一个表中。

       这个测试框架可以通过很少的代码来产生大量的测试用例。同样的代码在用数据表来产生各个测试用例的同时被复用。

混合测试自动化框架

  最普遍的执行框架是上面介绍的所有技术的一个结合,取其长处,弥补其不足。这个混合测试框架是由大部分框架随着时间并经过若干项目演化而来的。

unittest关于测试报告展示用例名称的细节:

在unittest有两个内置属性,可以自定义用例名称

之前的效果:

 

使用内置属性效果:

 

框架设计目标

  设计出来的框架是直接给测试人员,而且其他的测试人员只需要简单的向里面不断的补充测试用例即可;所以我们的框架设计必须三简化即操作简单,维护简单,扩展简单。

  设计框架的同时一定要结合业务流程,而不仅仅靠技术实现,其实技术实现不难,难点对业务流程的理解和把握。

  设计框架时要将基础的封装成公用的,如:get请求、post请求和断言封装成同基础通用类。

  测试用例要与代码分开,这样便于用例管理,采用数据驱动框架实现。

如下图所示:

  通过在excel录入测试用例,框架运行后自动进行用例执行,产生html网页版本的测试报告。

报告结果:

框架用到的技术点

1、语言:python

2、测试框架:unittest(assertEqual)或pytest

3、接口调用:requests(API非常简洁)

4、数据驱动:paramunittest (组装一定的格式数据就可以参数化)

5、数据管理:xlrd(读取excel文件数据)、configparser(读取配置文件)

6、数据格式的转换:ast,json

7、日志处理:logging ---清晰的执行过程,快速定位问题

8、测试报表:HTMLTestReportCN(由网友制作设计,显示清晰美观)

9、测试邮件发送测试报告:smtplib(邮件内容格式设置)、email(收发邮件)

10、持续集成:Jenkins(按策略执行接口测试脚本)

(推荐)混合测试自动化框架(关键字+数据驱动)

数据源实现:

数据源目前使用excel,数据如下:

链接:https://pan.baidu.com/s/1VvvGYRvGbElSlP6ngg0ktw
提取码:ppua

 自动化测试相关教程推荐:

2023最新自动化测试自学教程新手小白26天入门最详细教程,目前已有300多人通过学习这套教程入职大厂!!_哔哩哔哩_bilibili

2023最新合集Python自动化测试开发框架【全栈/实战/教程】合集精华,学完年薪40W+_哔哩哔哩_bilibili

测试开发相关教程推荐

2023全网最牛,字节测试开发大佬现场教学,从零开始教你成为年薪百万的测试开发工程师_哔哩哔哩_bilibili

postman/jmeter/fiddler测试工具类教程推荐

讲的最详细JMeter接口测试/接口自动化测试项目实战合集教程,学jmeter接口测试一套教程就够了!!_哔哩哔哩_bilibili

2023自学fiddler抓包,请一定要看完【如何1天学会fiddler抓包】的全网最详细视频教程!!_哔哩哔哩_bilibili

2023全网封神,B站讲的最详细的Postman接口测试实战教学,小白都能学会_哔哩哔哩_bilibili

思路:使用python读取excel数据;使用xlrd3

框架01:新建项目API_KEY_WORD_TEST_FRAME;

步骤1、在项目根目录下新建common的py文件夹和conf的普通文件夹;samples文件夹是用来写测试代码的demo;

步骤2、在conf下新建config.ini文件

 编写代码:

[default]

# 主机地址
hosts = api.weixin.qq.com
步骤3、在common下新建ini_file_utils.py文件和config_utils.py文件

ini_file_utils.py文件如下:

 总结:

 光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

如果对你有帮助的话,点个赞收个藏,给作者一个鼓励。也方便你下次能够快速查找。

如有不懂还要咨询下方小卡片,博主也希望和志同道合的测试人员一起学习进步

在适当的年龄,选择适当的岗位,尽量去发挥好自己的优势。

我的自动化测试开发之路,一路走来都离不每个阶段的计划,因为自己喜欢规划和总结,

测试开发视频教程、学习笔记领取传送门!!

 编写代码:

复制代码

# encoding: utf-8
# @author: Jeffrey
# @file: ini_file_utils.py
# @time: 2022/8/4 22:23
# @desc: 读取、写入ini文件
import os
import configparser


class IniFileUtils:  #和框架业务无关的底层代码==》公共底层代码

    def __init__(self,file_path):
        self.ini_file_path = file_path
        self.conf_obj = configparser.ConfigParser()
        self.conf_obj.read(self.ini_file_path, encoding='utf-8')

    def get_config_value(self,section, key):
        value = self.conf_obj.get(section, key)
        return value

    def set_config_value(self,section, key, value):
        '''设置config.ini文件中的值'''
        self.conf_obj.set(section, key, value)
        config_file_obj = open(self.ini_file_path, 'w')
        self.conf_obj.write(config_file_obj)
        config_file_obj.close()

if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    config_file_path = os.path.join(current_path, '../conf/config.ini')
    ini_file = IniFileUtils(config_file_path)
    print(ini_file.get_config_value('default', 'HOSTS'))

复制代码

config_utils.py文件如下:

 编写代码:

复制代码

# encoding: utf-8
# @author: Jeffrey
# @file: config_utils.py
# @time: 2022/8/4 22:26
# @desc: 封装读取ini文件的方法

import os
from common.ini_file_utils import IniFileUtils


current_path = os.path.dirname(os.path.abspath(__file__))
config_file_path = os.path.join(current_path, '../conf/config.ini')


class LocalConfig():  # #和框架业务有关系的底层代码

    def __init__(self,file_path = config_file_path):
        self.ini_file_obj = IniFileUtils(file_path)

    @property
    def get_hosts(self):
        '''获取ini文件中的hosts值'''
        hosts_value = self.ini_file_obj.get_config_value('default', 'hosts')
        return hosts_value


local_config = LocalConfig()

if __name__ == '__main__':
    print(local_config.get_hosts)

复制代码

步骤4、在samples文件下编写线性脚本,读取excel中的合并单元格

Excel表格如下:

 

 

 

 编写代码:

复制代码

# encoding: utf-8
# @author: Jeffrey
# @file: demo01.py
# @time: 2022/8/7 14:53
# @desc:  excel中合并单元格的读取
import xlrd3


work_book = xlrd3.open_workbook('test_data.xlsx')  # 创建一个工作簿对象
sheet_obj = work_book.sheet_by_name('Sheet1')  # 创建一个表格对象
print(sheet_obj.cell_value(1,2))  # 获取单元格的值(行,列),从0开始计算,行列实际序号-1
print(sheet_obj.cell_value(7,3))  # 获取单元格的值

print(sheet_obj.cell_value(1,0))  # 合并的单元格,获取的值为空
print(sheet_obj.cell_value(7,0))  # 合并的单元格,获取的值为空

# 包含四个元素(起始行,结束行,其实列,结束列) 包前不包后
print(sheet_obj.merged_cells)  # [(1, 5, 0, 1), (5, 9, 0, 1)]
# 思路步骤一:判断一个单元格是否是合并的单元格
x = 3 ; y = 0
if x>=1 and x<5:
    if y>=0 and y<1:
        print('合并单元格')
    else:
        print('非合并单元格')
else:
    print('非合并单元格')

# 思路步骤二:for循环的写法
for (min_row,max_row,min_col,max_col) in [(1, 5, 0, 1), (5, 9, 0, 1)]:
    print(min_row,max_row,min_col,max_col)

# 思路步骤三:把思路一和二整合进单元格判断是否是合并的单元格
x = 6 ; y = 0
for (min_row,max_row,min_col,max_col) in sheet_obj.merged_cells:
    if x >= min_row and x < max_row:
        if y >= min_col and y < max_col:
            print('合并单元格')
            break
        else:
            print('非合并单元格')
    else:
        print('非合并单元格')

# 思路步骤四:让合并单元格的值都等于合并第一个单元格的值,非合并单元格为原值
x = 4 ; y = 1
cell_value = None
for (min_row,max_row,min_col,max_col) in sheet_obj.merged_cells:
    if x >= min_row and x < max_row:
        if y >= min_col and y < max_col:
            cell_value = sheet_obj.cell_value(min_row,min_col)
            break
        else:
            cell_value = sheet_obj.cell_value(x, y)
    else:
        cell_value = sheet_obj.cell_value(x, y)
print(cell_value)

# 把上诉代码做成一个方法
def get_merged_cell_value(row_index,col_index):
    cell_value = None
    for (min_row, max_row, min_col, max_col) in sheet_obj.merged_cells:
        if row_index >= min_row and row_index < max_row:
            if col_index >= min_col and col_index < max_col:
                cell_value = sheet_obj.cell_value(min_row, min_col)
                break
            else:
                cell_value = sheet_obj.cell_value(row_index, col_index)
        else:
            cell_value = sheet_obj.cell_value(row_index, col_index)
    return cell_value

print(get_merged_cell_value(8,0))


# 获取excel中所有的数据 线性脚本
# 步骤一:线性脚本
head = sheet_obj.row_values(0)
print(head)  # ['学习课程', '步骤序号', '步骤操作', '完成情况']
excel_list = []
excel_dict = {}
excel_dict[head[0]] = get_merged_cell_value(1,0)
excel_dict[head[1]] = get_merged_cell_value(1,1)
excel_dict[head[2]] = get_merged_cell_value(1,2)
excel_dict[head[3]] = get_merged_cell_value(1,3)
excel_list.append(excel_dict)
print(excel_list)

# 步骤二: 使用for循环封装
head = sheet_obj.row_values(0)
excel_data_list = []
for j in range(1,sheet_obj.nrows):
    row_value_dict = {}
    for i in range(sheet_obj.ncols):
        row_value_dict[head[i]] = get_merged_cell_value(j,i)
    excel_data_list.append(row_value_dict)
print(excel_data_list)

for data in excel_data_list:
    print(data)

复制代码

步骤5、在common下新建excel_file_utils.py文件

 

 编写代码:

复制代码

# encoding: utf-8
# @author: Jeffrey
# @file: excel_file_utils.py
# @time: 2022/8/7 15:52
# @desc: 封装读取excel文件
import os
import xlrd3


class ExcelFileUtils():

    def __init__(self,excel_file_path, sheet_name):
        self.excel_file_path = excel_file_path
        self.sheet_name = sheet_name
        self.sheet_obj = self.get_sheet()

    def get_sheet(self):
        '''根据excel路径已经表名称 创建一个表格对象'''
        workbook = xlrd3.open_workbook(self.excel_file_path)
        sheet = workbook.sheet_by_name(self.sheet_name)
        return sheet

    def get_row_count(self):
        '''获取表格实际行数'''
        row_count = self.sheet_obj.nrows
        return row_count

    def get_col_count(self):
        '''获取表格的实际列数'''
        col_count = self.sheet_obj.ncols
        return col_count

    def get_merged_cell_value(self,row_index, col_index):
        '''
        :param row_index: 行下标
        :param col_index: 列下标
        :return: 获取单元格的内容
        '''
        cell_value = None
        for (min_row, max_row, min_col, max_col) in self.sheet_obj.merged_cells:
            if row_index >= min_row and row_index < max_row:
                if col_index >= min_col and col_index < max_col:
                    cell_value = self.sheet_obj.cell_value(min_row, min_col)
                    break
                else:
                    cell_value = self.sheet_obj.cell_value(row_index, col_index)
            else:
                cell_value = self.sheet_obj.cell_value(row_index, col_index)
        return cell_value

    def get_all_excel_data_list(self):
        '''获取excel中所有的数据,以列表嵌套字典的形式'''
        excel_data_list = []
        head = self.sheet_obj.row_values(0)
        for j in range(1,self.get_row_count()):
            row_value_dict = {}
            for i in range(self.get_col_count()):  # sheet_obj.ncols 动态获取表格多少列
                row_value_dict[head[i]] = self.get_merged_cell_value(j,i)
            excel_data_list.append(row_value_dict)
        return excel_data_list


if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    file_path = os.path.join(current_path, '../samples/test_data.xlsx')
    excel_obj = ExcelFileUtils(file_path, 'Sheet1')
    print(excel_obj.get_all_excel_data_list())
    print(excel_obj.get_col_count())

复制代码

测试执行结果:

步骤6、在项目根目录下新建test_data普通文件夹,把测试用例文件放里面

 

步骤7、在common下新建testcase_data_utils.py文件把测试用例的数据转换成框架需要用到的格式

 编写代码:

复制代码

# encoding: utf-8
# @author: Jeffrey
# @file: testcase_data_utils.py
# @time: 2022/8/7 17:02
# @desc: 数据格式转换:把测试用例数据转换成框架需要用到的格式
import os
from common.excel_file_utils import ExcelFileUtils

current_path = os.path.dirname(__file__)
file_path = os.path.join(current_path, '../test_data/testcase_infos.xlsx')


class TestCaseDataUtils:

    def __init__(self):
        self.test_data = ExcelFileUtils(excel_file_path=file_path,
                                        sheet_name='Sheet1')\
            .get_all_excel_data_list()

    def convert_testcase_data_to_dict(self):
        '''把excel中的测试用例数据转换为[{},{},{}]转换成{"":[],"":[],"":[]}'''
        test_case_data_dict = {}
        for row_data in self.test_data:
            test_case_data_dict.setdefault( row_data['测试用例编号'],[] )\
                .append(row_data)
        return test_case_data_dict

    def convert_testcase_data_to_list(self):
        '''把数据{"":[],"":[],"":[]} 转换成["case_id":key,"case_step":value]'''
        test_case_data_list = []
        for key, value in self.convert_testcase_data_to_dict().items():
            case_info_dict = {}
            case_info_dict['case_id'] = key
            case_info_dict['case_step'] = value
            test_case_data_list.append(case_info_dict)
        return test_case_data_list




if __name__ == '__main__':
    # print(TestCaseDataUtils().convert_testcase_data_to_dict())
    print(TestCaseDataUtils().convert_testcase_data_to_list())

复制代码

convert_testcase_data_to_dict()方法的目的是把用例数据以用例编号和用例数据分开

'''把excel中的测试用例数据转换为[{},{},{}]转换成{"":[],"":[],"":[]}'''

查看执行结果:

convert_testcase_data_to_list()方法的目的是以用例编号和用例步骤分开

'''把数据{"":[],"":[],"":[]} 转换成["case_id":key,"case_step":value]'''

转换后数据

框架02,封装requests请求,在common下新建一个requests_utils.py文件

步骤1,封装get请求

 编写代码:

复制代码

# encoding: utf-8
# @author: Jeffrey
# @file: requests_utils.py
# @time: 2022/8/7 21:42
# @desc: 封装requests 请求
import json

import requests
from common.config_utils import local_config


class RequestsUtils:

    def __init__(self):
        self.hosts = local_config.get_hosts
        self.session = requests.session()

    def get(self,requests_info):

        url = 'https://%s%s' % (self.hosts, requests_info['请求地址'])
        # 使用json.loads()把字符串转成字典
        url_params = json.loads(requests_info['请求参数(get)'])
        # 使用三元运算符判断请求头部信息为空,返回空字典{}
        header_info = ( json.loads(requests_info['请求头部信息'])
                            if requests_info['请求头部信息'] else {} )
        response = self.session.get(url=url, params=url_params, headers=header_info)
        response.encoding = response.apparent_encoding  # 防止响应正文乱码
        print(response.text)


if __name__ == '__main__':
    requests_info = {'请求头部信息': '',
                     '请求地址': '/cgi-bin/token',
                     '请求参数(get)': '{"grant_type":"client_credential",'
                                  '"appid":"wxf1856",'
                                  '"secret":"92a113bd423c99"}'}
    result = RequestsUtils().get(requests_info)
    print(result)

复制代码

步骤2、把返回的所有响应信息,作为一个字典,且多一个code字段

 编写代码:

复制代码

result = {
    'code':0,
    'response_code':response.status_code,
    'response_info':response.reason,
    'response_headers':response.headers,
    'response_body':response.text }

复制代码

步骤3、封装post请求

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

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

相关文章

Leetcode 153. 寻找旋转排序数组中的最小值

class Solution {//因为最小值和最大值总是相邻的&#xff08;除了初始状态&#xff09;//1.用二分查找&#xff0c;如果右侧是有序则最小值在左侧//2.如果右侧无序则最小值在右侧//如果mid正好是最小值&#xff0c;那么右侧自然是有序的&#xff0c;//为了将mid加入到搜索的一…

如何用内容营销推动企业成长?媒介盒子教你三步实现

信息时代下每个人都能通过网络了解自己所需的信息&#xff0c;企业与受众的接触也更加直接&#xff0c;企业在获得更多消费者触达通道的同时&#xff0c;消费者也在经历信息爆炸和碎片化&#xff0c;如何在大量信息中脱颖而出&#xff0c;抓住消费者心智&#xff0c;成为许多品…

外卖小程序系统:数字化餐饮的编码之道

在当今数字化时代&#xff0c;外卖小程序系统成为了餐饮业的一项技术巨制。这个系统不仅提供了便捷的点餐体验&#xff0c;更通过先进的技术手段&#xff0c;实现了高效订单处理、实时配送追踪以及个性化推荐。让我们深入了解外卖小程序系统的技术魔法&#xff0c;一起揭秘数字…

API接口接入1688电商数据平台获取商品详情数据示例

1688电商数据平台是一个提供海量商品信息的数据平台&#xff0c;通过API接口可以方便地获取商品详情数据。以下是一个示例&#xff0c;演示如何接入1688电商数据平台&#xff0c;获取商品详情数据。 步骤一&#xff1a;注册1688账号并获取API权限 首先需要在1688电商数据平台…

ArkTS编程语法基础,让你成为HarmonyOS开发高手

文章目录 ArkTS简介ArkUI开发框架的整体架构ArkTS的基础类型条件语句函数类模块函数定义函数的参数箭头函数 迭代器后续学习资源介绍 ArkTS简介 ArkTS是HarmonyOS主力应用开发语言。它在TypeScript (简称TS)的基础上&#xff0c;匹配ArkUI框架&#xff0c;扩展了声明式UI、状态…

防火墙..

目录 1.什么是防火墙 1.1分类 1.2.Netfilter(数据包过滤) 1.2.1定义 1.2.2Netfilter分析内容 1.3防火墙无法完成的任务 1.4iptables 与firewalld区别 2.iptables 2.1iptables执行原则 2.1.1原则 2.1.2防火墙规则 2.2规则链 2.2.1概念 2.2.2分析 2.2.3规则链分类…

leetcode:反转链表

题目描述 题目链接&#xff1a;206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 分析题目 思路一 我们可以设计算法让整个链表掉头 定义三个代码n1,n2,n3 n1指向NULL&#xff0c;n2指向head&#xff0c;n3指向第二个结点 当n2不为NULL的时候&#xff0c;让n2->ne…

fastdfs-client-java-1.30 maven 打包安装

1. 进入源代码目录&#xff0c;打开cmd mvn clean install 或者 mvn package 问题不大的话会在同级目录target目录下生成打包后文件 2. 当前目录下cmd进行maven安装 mvn install:install-file -DgroupIdorg.csource -DartifactIdfastdfs-client-java -Dversion${version} -D…

CompletableFuture.join() vs Future.get(),开发中哪个更好

CompletableFuture和Future都是Java中的接口&#xff0c;用于异步编程和并发处理。 Future表示一种异步计算的结果&#xff0c;可以通过get()方法获取计算结果或等待计算的完成。但是&#xff0c;如果计算还未完成&#xff0c;get()方法会阻塞线程&#xff0c;这会影响并发性能…

虾皮台湾站点什么好卖

在如今的电商时代&#xff0c;越来越多的人选择通过网购来满足购物需求。而中国台湾地区作为一个充满机遇的市场&#xff0c;吸引了许多商家的目光。虾皮作为台湾地区最大的电商平台之一&#xff0c;为卖家提供了丰富的销售机会。但是&#xff0c;卖家们在选择什么产品来销售时…

文档明明在桌面上却不显示?5个方法轻松解决!

“我之前保存文档的时候明明选择保存在桌面&#xff0c;上次看的时候文件还在&#xff0c;但是今天打开电脑后发现我保存在桌面的文档不见了&#xff0c;这是为什么呢&#xff1f;还有机会找回我的文件吗&#xff1f;” 在日常使用电脑时&#xff0c;有些用户为了方便&#xff…

大势智慧代理商体系持续开疆拓土,全国代理火热招募中...

11月15日&#xff0c;武汉大势智慧科技有限公司&#xff08;后简称“大势智慧”&#xff09;与上海宝天信息科技有限公司&#xff08;后简称“宝天信息”&#xff09;金牌代理商签约授牌仪式成功举行。大势智慧副总裁周济安先生、宝天信息经理王芳女士分别作为双方签约代表出席…

virtuoso 后仿 ADE L error

ADE后仿时出现error ERROR (SFE-23): "input.scs" 299: The instance _57_D32_noxref is referencing an undefined model or subcircuit, parasitic_nwd. Either include the file containing the definition of parasitic_nwd, or define parasitic_nwd before run…

STM32定时器输入捕获测量高电平时间

STM32定时器输入捕获测量高电平时间 输入捕获测量高电平时间CuebMX配置代码部分 本篇内容要求读者对STM32通用定时器有一点理解&#xff0c;如有不解&#xff0c;请看 夜深人静学32系列15——通用定时器 输入捕获 输入捕获是STM32通用定时器的一种功能&#xff0c;可以捕获特定…

mysql数据库【进阶篇】

1.存储引擎 1.1 mysql的体系结构 连接层&#xff1a;最上层是一些客户端和链接服务&#xff0c;主要完成- -些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。服务层&#xff1a;第二层架构主要完成大多数的核心服务功…

Scrum敏捷开发培训团队和组织来说的重要性

Scrum敏捷开发培训对于团队和组织来说是至关重要的&#xff0c;有以下几点&#xff0c;大家可以参考下&#xff1a; 理解敏捷价值观和原则&#xff1a; 培训有助于团队理解敏捷方法背后的核心理念和价值观&#xff0c;包括个体和互动、工作软件、客户合作和响应变化。这有助于建…

解决Spring Boot应用在Kubernetes上健康检查接口返回OUT_OF_SERVICE的问题

现象 在将Spring Boot应用部署到Kubernetes上时&#xff0c;健康检查接口/actuator/health返回的状态为{"status":"OUT_OF_SERVICE","groups":["liveness","readiness"]}&#xff0c;而期望的是返回正常的健康状态。值得注…

单脉冲测角-和差比幅法-方向图传播因子-函数编写

方向图传播因子-函数编写 和差比幅法单脉冲测角原理代码仿真结果参数说明 和差比幅法单脉冲测角原理 有关单脉冲测角和差比幅法的原理已经在博文单脉冲测角-和差比幅法中详细介绍了&#xff0c;我们在实际仿真的时候&#xff0c;往往需要在给定来波方向下方向图转化因子&#…

关于代码混淆,看这篇就够了

​ 代码混淆一.基本概念java的bytecode很容易通过JAD等反编译工具还原出源代码。这样势必不满足安全的定义。如何一定程度上保护需要防止被反编译的源代码呢&#xff1f;混淆&#xff08;obfuscate&#xff09;技术。注意&#xff1a;用obfuscate防盗版是根本不可能&#xff0c…