2022/9/5开始第三版接口自动化(yaml用例)测试框架(记录搭建过程)(这个废弃了)

简介: 2022/9/5开始第三版接口自动化(yaml用例)测试框架(记录搭建过程)(这个废弃了)

序章

第一代框架

源码地址

教程

第二代框架

源码地址

搭建教程

使用教程

一、yaml用例设计

这个模板不代表最终模板,后续可能还有改动

#用例(名称)标题,需要参数依赖的全部设置字符格式,后期解析成字典列表等格式
用例标题:
  #接口地址,也支持参数依赖
  path: /test/$.id
  #请求方法
  method: post
  #有值就用这个,没用就使用默认配置的,有header里面使用参数依赖的场景
  header: {}
  # 是否运行
  is_run: True
  # 前置sql:sql有2大类,一个是查询有返回值,一个是增删改无返回值,前置sql为无返回值类型
  precondition_sql:
    - UPDATE case_test SET title = '标题2', ex = '44' WHERE id = 2
    - UPDATE case_test SET title = '标题3', ex = '55' WHERE id = 3
  #请求参数较多,这里就使用原始字典格式,除了提取表达式,其他的都带上引号,预防出错,random_time()随机函数使用
  data:
    {
      "id":$.id,
      "projectNo": "320SF000206004",
      "name":$.name,
      "time": random_time(),
      "str": random_str(6),
      "int": random_number(5),
      # 请求参数sql为有返回值查询
      "sql": sql-SELECT title FROM case_test where id=2
    }
  #参数类型 json 或者 form表单
  data_type: json
  #从接口返回结果提取哪些字段和提取表达式,比如从返回数据提取用户id和name
  extract_key:
    id: $.id
    name: $.name
  #断言表达式
  # 除了数字类型比较,其他的字符或者表达式全部带上引号,表达式必须要写在后面(必须按照格式来)
  # 断言sql为有返回值查询
  assert_expression:
    #判断响应码是否和预期一致
    code: 200
    #判断预期值是否在返回值里面(用值是否在接口返回的内容里面判断)
    body: 内容包含
    #判断json提取值是否和预期一致(用键和值比较等于,大于小于或者in判断)
    json:
      {"ig" : ">$.ig","rng" : "==$.rng","edg" : "<$.edg",'1':'in$.ig'}
    #判断sql查询值是否和预期一致(用键和值作判断,规则和上面一致)
    sql:
      {'1':">sql-SELECT title FROM case_test where id=2",'2':"==sql-SELECT title FROM case_test where id=2"}

二、配置哪些字段需要字符化

‘headers’,‘data’,'assert_expression’这三个字段是需要参数依赖的,要让它们被读取处理时先转成字符格式,方便后续使用.replace()替换方法,替换完后使用eval()方法去除字符恢复原来的格式

三、用例读出

yaml读出方法基本和第二版无改动

文件目录排除做了一些修改,增加了一个raw_case_path用例所属文件夹的配置

用例读取的方法进行了重构
增加了把用例的某些字段字符化的处理,用例的格式也变成了列表嵌套字典的格式

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
@time    : 2022/9/5 
@Author  : LL
@File    : read_file.py
'''
import os
from pathlib import Path
from config.config import exclude_file,exclude_dir,raw_case_path,str_zd
import yaml
class ReadFile:
    # D:\python2022\yaml_pytest_drf_vue/
    project_directory = str(Path(__file__).parent.parent) + '/'
    @classmethod
    def read_yaml(cls, path):
        '''读取yaml文件,以字典格式返回{'用例标题':{'path':'/test','data':{'id':1}}}'''
        try:
            path = cls.project_directory + path
            file = open(path, 'r', encoding='utf-8')
            with file as doc:
                content = yaml.load(doc, Loader=yaml.Loader)
                return content
        except Exception as e:
            # logger.error(f'读取{path}文件出错:{e},检查文件内容格式是否错误')
            print(f'读取{path}文件出错:{e},检查文件内容格式是否错误')
    @classmethod
    def file_execute_list(cls, exclude_file=exclude_file, exclude_dir=exclude_dir,raw_case_path=raw_case_path):
        '''
        :param exclude_dir: 要排除的目录(二级目录)
        :param exclude_file: 要排除的文件(case目录下所有文件)
        :param raw_case_path: 用例原始文件总目录
        :return: 获取case下的所有用例文件列表,最多支持二级目录,通用排除文件返回最终要执行的用例文件
        '''
        file_list = []
        case_path = cls.project_directory + raw_case_path
        # case目录下的所有文件
        for filename in os.listdir(case_path):
            if 'yaml' in filename:
                # 要储存为case开头的目录,方便读取用例使用
                file_list.append(raw_case_path + filename)
            else:
                # 遍历case下面的二级目录
                for i in os.listdir(case_path + '/' + filename):
                    # 检查这个二级目录是否需要被排除
                    if filename in exclude_dir:
                        continue
                    # 要储存为case开头的目录,方便读取用例使用,这是二级目录得把二级目录拼接上
                    file_list.append(raw_case_path + filename + '/' + i)
        # 文件列表不为空的话一个一个的排除掉这些文件
        if exclude_file != []:
            for i in exclude_file:
                file_list.remove(i)
        return file_list
    @classmethod
    def read_case(cls):
        '''最终返回列表嵌套字段的用例格式'''
        #读取用例要执行的列表
        path_list = cls.file_execute_list()
        case_list=[]
        for i in path_list:
            #i就是单yaml文件路径,然后读出来放到用例列表里面
            case=cls.read_yaml(i)
            # print(case)
            for k,v in case.items():
                #由于yaml是以用例标题为键,直接赋予名字,然后把标题加到主体里面去
                case_title=k
                case_body=v
                #这里面有需要字符化的字段,字典,列表等全部转成字符串
                # data_d = type(case_body['data'])
                # print(f'测试下数据是不是转化成功-转换后类型{data_d}')
                for str_z in str_zd:
                    try:
                        case_body[str_z]=str(case_body[str_z])
                    except KeyError as e:
                        print(f'用例无字段{e}')
                case_body.update({'case_title':case_title})
                # data_s=type(case_body['data'])
                # print(f'测试下数据是不是转化成功-转换后类型{data_s}')
                #判断用例是否需要运行
                if case_body['is_run']==True:
                    #最后把case_body加入到case_list里面
                    case_list.append(case_body)
        return case_list
if __name__ == '__main__':
    case = ReadFile.read_yaml('raw_case_file/test.yaml')
    # print(type(str(case['用例标题']['data'])))
    # print((str(case['用例标题']['data'])))
    print(ReadFile.file_execute_list())
    ReadFile.read_case()

四、requests二次封装

对比第二版的requests二次封装进行了重构,添加支持get请求传参,支持form表格和json传参,增加put和delete方法

五、pytest基本使用配置

pytest运行命令,详细展示运行详情,生成报告数据到./report/data目录

addopts = -vs --alluredir ./report/data

设置要运行用例文件的路径,我这里就一个文件,指定个目录就行

testpaths = case_run

简单的一个主方法,可以看到控制台打印出了三条用例数据,把从yaml文件里面读取的用例使用parametrize参数化

六、参数提取和替换

参数替换:相比于第二代重构了这个方法,替换是先把要替换的数据字符化,然后通过正则查找到需要替换的数据,然后去参数池里面取到,替换到原数据中,最后解除字符化,变为原来的格式

参数提取:把提取表达式转化为具体的值,把提取的值保存到参数池里面

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
参数替换使用正则表达式,参数池格式为${},函数格式为f{},提取使用jsonpath
'''
from jsonpath import jsonpath
import re
class ParameterSetting:
    # 参数池
    access_value = {}
    @classmethod
    def data_is_replace(cls, data):
        '''
        :param data: 请求参数data和提取参数extract_key
        :return: 请求参数是否需要被替换
        '''
        if data is None:
            return False
        for k, v in data.items():
            if '&{' in v or 'f{' in v:
                return True
        return False
    # 基于jsonpath表达式提取
    @classmethod
    def extract(cls, api_response: dict, extract_key: dict):
        '''
        :param api_response: 接口返回值 例:{'code':200,'content':{'bill':'ys123456'}}
        :param extract_key:  提取表达式 例:{'bill': '$.content.bill'}
        '''
        extract_value = {}
        # 把提取表达式转化为具体的值 例:{'bill': 'ys123456'}
        for k, v in extract_key.items():
            extract_value[k] = jsonpath(api_response, v)[0]
        # 把提取的值保存到参数池里面
        for k, v in extract_value.items():
            cls.access_value[k] = v
    # 基于正则表达式替换
    @classmethod
    def replaces(cls, data: str):
        #这里${}是要从参数池里面读取的并且替换的数据
        replace_json_list = re.findall('\${(.*?)}', data)
        replace_f_list = re.findall('\f{(.*?)}', data)
        if len(replace_json_list)>0:
            #把要替换的值变成jsonpath表达式,从参数池里面读取,然后替换
            for i in replace_json_list:
                #i就是${}里面的值,'$.'+i把它拼接成jsonpath表达式
                print(i)
                #jsonpath(cls.access_value,'$.'+i)[0]就是从参数池里面取出的值
                print(jsonpath(cls.access_value,'$.'+i)[0])
                #f'${"{"+i+"}"}'就是要被替换的值 例${cc},这里有语法冲突,我使用字符拼接了
                data=data.replace(f'${"{"+i+"}"}',jsonpath(cls.access_value,'$.'+i)[0])
        return eval(data)
#方法测试
if __name__ == '__main__':
    print(f'提取前参数池{ParameterSetting.access_value}')
    api_response={'code':200,'content':{'bill':'ys123456','aa':'aa的值'},'bb':'bb的值','cc':'cc的值'}
    extract_key={'bill': '$.content.bill','bb':'$.bb','cc':'$.cc','aa':'$.content.aa'}
    ParameterSetting.extract(api_response,extract_key)
    print(f'提取后参数池{ParameterSetting.access_value}')
    data={
        's':'${aa}',
        'v':['${bb}','${cc}']
    }
    print(ParameterSetting.replaces(str(data)))
    print(type(ParameterSetting.replaces(str(data))))

测试

问题:yaml格式错误

修改用例模板,这个格式会报错

解决方案直接加引号,直接在这里就把他字符化,不用外面再去字符化了,headers和assert_expression里面的json也这样处理,注意了,里面的引号就得是双引号了

那之前设置的字符化配置就失去意义了,先不动,有影响在改

七、新的用例模板

#用例(名称)标题,需要参数依赖的全部设置字符格式,后期解析成字典列表等格式
用例标题0:
  #接口地址,也支持参数依赖
  path: /test/{id}
  #请求方法
  method: post
  #有值就用这个,没用就使用默认配置的,有header里面使用参数依赖的场景
  headers: '{"token":"${token}"}'
  # 是否运行
  is_run: True
  # 前置sql:sql有2大类,一个是查询有返回值,一个是增删改无返回值,前置sql为无返回值类型
  #  precondition_sql:
  #    - UPDATE case_test SET title = '标题2', ex = '44' WHERE id = 2
  #    - UPDATE case_test SET title = '标题3', ex = '55' WHERE id = 3
  #请求参数较多,这里就使用原始字典格式,除了提取表达式,其他的都带上引号,预防出错,random_time()随机函数使用
  data:
    '
    {
          "id":${id},
          "projectNo": "320SF000206004",
          "name": ${name},
          "time": random_time(),
          "str": random_str(6),
          "int": random_number(5),
          # 请求参数sql为有返回值查询
          "sql": sql-SELECT title FROM case_test where id=2
        }
    '
  #参数类型 json 或者 form表单
  data_type: json
  #从接口返回结果提取哪些字段和提取表达式,比如从返回数据提取用户id和name
  extract_key:
    id: $.id
    name: $.name
  #断言表达式
  # 除了数字类型比较,其他的字符或者表达式全部带上引号,表达式必须要写在后面(必须按照格式来)
  # 断言sql为有返回值查询
  assert_expression:
    #判断响应码是否和预期一致
    code: 200
    #判断预期值是否在返回值里面(用值是否在接口返回的内容里面判断)
    body: 内容包含
    #判断json提取值是否和预期一致(用键和值比较等于,大于小于或者in判断)
    json:
      '{"ig": ${name},"rng": ${name},"edg": "${name}"}'
    #判断sql查询值是否和预期一致(用键和值作判断,规则和上面一致)
#    sql:
#      {'1':">sql-SELECT title FROM case_test where id=2",'2':"==sql-SELECT title FROM case_test where id=2"}

八、断言处理

和第二版相比除了类名其他全部不同

通过接口返回先获取到响应码,响应json信息,响应文本信息

断言code判断是否相等就行,断言body判断内容是否在返回文本里面就行

这里要修改下之前的requests方法,之前这个都是固定返回json,现在返回整个对象信息,使用时要自行携带token

json断言处理

这里也需要用到之前的正则提取,之前参数替换是用正则在参数池里面找数据,在断言这里就是在接口的返回参数里面找数据

我把方法优化了一下,把通用的地方提取了出来

之前的方法改成了这样

json断言写完,等待测试

九、组装请求,参数替换和断言

导包

启动下mock接口

在请求时想到,如果不需要依赖的参数在读出的headers和data等信息是字符格式的,这样请求requests就会报错 增加下字符转化,以防万一,这个url组成先改掉,后续加上配置后在改回来

组装完成,感觉会有很多报错

十、开始运行和排坑(重要,内容很多)

以前只在这里判断了的请求参数是否需要依赖,我现在是判断所有的用例数据是否需要依赖

直接重构,使用字符格式判断

看请求信息

问题: 参数位置颠倒

到这来来发现位置被调换了,data和headers的信息

我把data调整到headers前面再测试

每填就是None,判断这里改下,返回成功

问题: 提前使用json()方法

这里报错,因为我提前使用了json()方法,导致没有status_code属性而报错

删除下,把断点换个地方

可以看到这个对象的三个属性值

问题: 无效的转义

无效的转义,之前单独测试没啥问题,现在加个r原生字符re.findall(r’${(.*?)}', data)就好了

这里开始三个字段进行了字符化处理,导致这里字符格式使用了列表格式的索引方法,把之前那个断言字段的字符化去掉

json断言信息就传进来了

正则未提取到值

问题: 用例格式写错

格式写错了

现在就有了

这里k和v是不等的,断言就失败了

修改下用例,让断言通过

get不带参数的用例运行通过,但是还没有提取参数

问题: 之前的判断是否需要提取方法无法使用

写一个判断是否需要提取的方法,之前那个不能用了

使用和测试

问题:替换判断字符写错

使用post请求,参数替换,这里忘记改了,导致参数没有替换

参数替换把断言里面的也给替换了,需要区分一下,断言的参数是在接口返回里面拿的,我直接在这里先把断言部分数据删除掉

是否需要替换参数也需要改下

这里要接一下,不然参数就没有替换成功了

问题:requests封装有误

这里发现我虽然在用例里面写的post,但是还是用的get,这个方法这样写有问题,得改下

修改后

十一、请求基本信息配置

对于用例里面的header信息采用追加的方式加入到这里,然后发起请求

信息全读出来

增加一个配置信息,配置当前测试的环境

最后把信息返回出去

在requests里面调用和处理

相关文章
|
3天前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
4天前
|
前端开发 关系型数据库 测试技术
django集成pytest进行自动化单元测试实战
在Django项目中集成Pytest进行单元测试可以提高测试的灵活性和效率,相比于Django自带的测试框架,Pytest提供了更为丰富和强大的测试功能。本文通过一个实际项目ishareblog介绍django集成pytest进行自动化单元测试实战。
13 3
django集成pytest进行自动化单元测试实战
|
3天前
|
人工智能 自然语言处理 测试技术
基于LangChain手工测试用例转接口自动化测试生成工具
本文介绍利用大语言模型自动生成接口自动化测试用例的方法。首先展示传统通过HAR文件生成测试用例的方式及其局限性,随后提出结合自然语言描述的测试需求与HAR文件来生成更全面的测试脚本。通过LangChain框架,设计特定的提示词模板,使模型能够解析测试需求文档和HAR文件中的接口信息,并据此生成Python pytest测试脚本。示例展示了正常请求、非法请求及无效路径三种测试场景的自动化脚本生成过程。最终,整合流程形成完整代码实现,帮助读者理解如何利用大模型提高测试效率和质量。
14 2
|
7天前
|
IDE 测试技术 持续交付
Python自动化测试与单元测试框架:提升代码质量与效率
随着软件行业的发展,代码质量和效率变得至关重要。自动化测试与单元测试是保证质量、提升效率的关键。Python凭借其简洁强大及丰富的测试框架(如Selenium、Appium、pytest和unittest等),成为了实施自动化测试的理想选择。本文将深入探讨这些框架的应用,帮助读者掌握编写高质量测试用例的方法,并通过持续集成等策略提升开发流程的效率与质量。
26 4
|
7天前
|
Web App开发 IDE 测试技术
天呐!当揭开 Selenium 自动化测试框架的神秘面纱,设计与实现令人瞠目!
【8月更文挑战第12天】Selenium 是一强大自动化测试框架,用于Web应用测试。它含WebDriver、IDE和Grid等工具,支持Chrome、Firefox等浏览器。可通过编程模拟用户交互验证应用功能。例如使用Python结合Selenium WebDriver编写自动化测试脚本,实现打开网页、操作元素及断言等功能。还可结合测试框架和Selenium Grid提升测试效率和并行执行能力。
19 1
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
软件测试的未来之路:自动化与智能化的融合之旅
随着技术的飞速发展,软件测试领域正经历着一场革命。传统的手动测试方法逐渐让位于更加高效、智能的自动化测试策略。本文将探讨自动化测试工具的演进,以及人工智能如何赋能未来的软件测试实践,提升测试效率和准确性。我们将通过实例分析,了解自动化测试工具的现状,探索AI技术在测试中的应用,并展望未来软件测试的趋势。
21 2
|
3天前
|
机器学习/深度学习 人工智能 自然语言处理
探索软件自动化测试的未来:AI驱动的测试策略
【7月更文挑战第47天】 随着人工智能(AI)技术不断进步,其在软件测试领域的应用也日益广泛。本文将探讨如何整合AI技术与现有的自动化测试流程,提出一个面向未来的测试策略。文章重点分析了AI在测试用例生成、执行、结果分析和持续集成中的作用,同时预测了这种技术融合对测试工程师角色的影响,以及它如何提高软件测试的效率和准确性。
|
3天前
|
机器学习/深度学习 人工智能 算法
软件测试的未来:AI与自动化的融合之路
随着技术的不断进步,人工智能(AI)和自动化技术在软件测试领域的应用日益增多。本文将探讨AI如何改变软件测试的面貌,以及自动化工具如何助力提升测试效率和准确性。我们将从实际案例出发,分析AI和自动化技术带来的优势与挑战,并展望未来软件测试的发展趋势。
|
7天前
|
机器学习/深度学习 人工智能 算法
软件测试的未来:探索自动化与人工智能的融合
在数字化时代的浪潮中,软件测试领域正经历着一场革命性的变革。本文将带领读者一探究竟,从传统的手工测试方法,到自动化测试的崛起,再到人工智能技术的融入,我们一同见证这场技术演进如何重塑软件测试的未来。文章不仅阐述了自动化和AI技术在提高测试效率、准确性方面的优势,还深入探讨了这些技术带来的挑战和机遇,为读者呈现一个全面而深刻的软件测试新世界。
20 0
|
8天前
|
存储 测试技术 API
apifox实例应用-自动化测试用例for循环的使用
总结来说,通过在Apifox自动化测试用例中结合for循环的使用,我们可以有效地对接口进行批量测试,提升测试效率和覆盖率。同时,通过参数化测试数据的灵活应用,能够确保我们的接口在不同的输入条件下都能保持正确的行为。这种方法能够显著减少手动测试工作量,同时通过标准化的流程确保测试的一致性。
17 0