首页 > 其他分享 >关键字 开发-12 yaml文件实现参数化

关键字 开发-12 yaml文件实现参数化

时间:2023-12-15 18:05:25浏览次数:28  
标签:case 12 parameters 关键字 config yaml params fixtures data

前言

说到接口自动化,那肯定少不了参数化,这也是pytest的一个特色之一,相比与unitest实现起来更加方便好用。实验参数化常见的就是使用@pytest.mark.parametrize在测试函数或类中定义多组参数,在用例中实现参
数化。

# 参数化方式一

import pytest
@pytest.mark.parametrize("test_input,expected",[ ["3+5", 8], ["2+4", 6], ["6 * 9", 42] ])
def test_eval(test_input, expected):
  assert eval(test_input) == expected

1. 使用pytest_generate_tests实现参数化


由官方文档,可以知道,实现参数化的方式有三种,我们使用第三种方式进行实验,可以更适合本框架的开发。具体的实现方式如官方详细文档,实现的步骤就是:

1.在test函数中传指定的参数名称
2.conftest中实现hook函数:pytest_generate_tests
3.传入参数化数据,实现参数化

1.1 实现参数化示例

# test_demo3.py
# 用例的id名称
names = ["login nam1", "login name2"]
# 测试数据 list of dict
test_data = [{
    "url": "http://49.235.x.x:5000/api/v1/login/",
    "method": "POST",
    "json": {
    "username": "test",
    "password": "123456"
    }
},
{
    "url": "http://49.235.x.x:5000/api/v1/login/",
    "method": "POST",
    "json": {
    "username": "test",
    "password": "123456"
    }
}]
def test_login(param):
    print('---->', param)
# conftest.py
def pytest_generate_tests(metafunc):
  """ generate (multiple) parametrized calls to a test function."""
  if "param" in metafunc.fixturenames:
    metafunc.parametrize("param",
      metafunc.module.test_data,
      ids=metafunc.module.names,
      scope="function")

2 在yaml中实现参数化

在hook函数中,我们分为module级别的参数化,以及function级别的参数化,module级别就是在yml文件的config中传入的参数,function级别就是在单个用例中传入的参数,所以都支持。
所以可以知道,在yml中要实现参数化,需要解决2个问题:

1.在用例函数中需要传入fixtures参数,也就是需要将参数写在fixtures中,再传入用例函数。
2.在创建函数时,要设置参数属性,以及参数数据属性,即:在生成动态用例函数时,就要添加这2个属性

# conftest.py
def pytest_generate_tests(metafunc):
    """
    测试用例参数化:
    :param metafunc: 共有五个属性值
    1.matafunc.fixturenames: 参数化收集时的参数名称
    2.matafunc.module: 使用参数名称进行参数化的测试用例所在的模块对象
    3.matafunc.config: 测试用例会话
    4.matafunc.function: 测试用例对象,即函数或方法对象
    5.matafunc.cls: 测试用例所属的类的类对象
    :return: None
    """
    if hasattr(metafunc.module, 'module_params_data'):  # 检查函数对象
        params_data = getattr(metafunc.module, 'module_params_data')
        params_fixtures = getattr(metafunc.module, 'module_params_fixtures')

        params_len = 0  # 参数化,参数的个数
        if isinstance(params_data, list):
            if isinstance(params_data[0], list):  # 支持下面这种格式
                """fixtures: username, password
                   parameters:- [test1, '123456']
                              - [test2, '123456']"""
                params_len = len(params_data[0])
            elif isinstance(params_data[0], dict):  # 支持下面这种格式
                """parameters:
                            - {"username": "test1", "password": "123456"}
                            - {"username": "test2", "password": "1234562"}"""
                params_len = len(params_data[0].keys())
            else:
                params_len = 1
        params_args = params_fixtures[-params_len:]

        metafunc.parametrize(
            ','.join(params_args),
            params_data,
            scope="module"
        )
    # 用例级别
    if hasattr(metafunc.module, f'{metafunc.function.__qualname__}_params_data'): # __qualname__:获取用例函数名
        params_data = getattr(metafunc.module, f'{metafunc.function.__qualname__}_params_data')
        params_fixtures = getattr(metafunc.module, f'{metafunc.function.__qualname__}_params_fixtures')
        params_len = 0  # 参数化,参数的个数
        if isinstance(params_data, list):
            if isinstance(params_data[0], list):  # 支持下面这种格式
                """fixtures: username, password
                   parameters:- [test1, '123456']
                              - [test2, '123456']"""
                params_len = len(params_data[0])
            elif isinstance(params_data[0], dict):  # 支持下面这种格式
                """parameters:
                            - {"username": "test1", "password": "123456"}
                            - {"username": "test2", "password": "1234562"}"""
                params_len = len(params_data[0].keys())
            else:
                params_len = 1
        params_args = params_fixtures[-params_len:]
        metafunc.parametrize(
            ','.join(params_args),
            params_data,
            scope="function"
        )

在run.py文件中我们分别添加module级别的和用例级别的代码。

  def run(self):

    ...
    # 收集config中的fixtures
        config_fixtures = self.raw.get('config').get('fixtures', [])
        # 多个则变成list形式存储
        if isinstance(config_fixtures, str):
            config_fixtures = [item.strip(' ') for item in config_fixtures.split(',')]  # 这里做了优化,需要去掉item中的" ",否则fixture传入的不合法

    # 在收集config——fixtures这里,我们添加收集parameters参数data
    config_param_data = self.raw.get('config').get('parameters', [])
    # 2.先渲染config_variables

    ...

    # # --------------给module加参数化-------
    config_param_data = render_template_obj.rend_template_any(config_param_data, **self.context)
    config_param_fixtures = render_template_obj.rend_template_any(config_fixtures, **self.context)
    # # config中支持2种格式,把他们合并然后返回一种统一处理
    config_param_fixtures, config_param_data = self.parameters_data(config_param_fixtures, config_param_data)
    if config_param_data:
          # 向module中加入参数化的属性
          setattr(self.module, 'module_params_data', config_param_data)
          setattr(self.module, 'module_params_fixtures', config_param_fixtures)
     # # -------------------end----------------

    ...
    # 在用例这里,添加用例级别的代码
    case = {}  # 收集用例名称和执行内容
    for case_name, case_value in self.raw.items():
      ...
    # 用例中的fixtures
    case_fixtures = []
    if 'fixtures' in case[case_name][0]:  # fixtures写在第一个步骤中
        case_raw_fixtures = case[case_name][0].get('fixtures', [])
        case_fixtures = render_template_obj.rend_template_any(case_raw_fixtures, **self.context)
        # 如果有多个,则变成list的形式
        if isinstance(case_fixtures, str):  # fixtures: run_fixt or [run_fixt1, run_fixt2] --> list  # 这里做了修改优化,放到了if层级下
            case_fixtures = case_fixtures.split(',')
    # --------------用例级别的参数化----------
    if 'parameters' in case[case_name][0]:
        case_raw_parameters = case[case_name][0].get('parameters', [])
        case_parameters = render_template_obj.rend_template_any(case_raw_parameters, **self.context)
        case_fixtures, case_parameters = self.parameters_data(case_fixtures, case_parameters)
        if case_parameters:
            # 向module中加入参数化数据和属性
            setattr(self.module, f'{case_name}_params_data', case_parameters)
            setattr(self.module, f'{case_name}_params_fixtures', case_fixtures)
    # -------------end--------------------

    # 新增函数:对传入的2种方式,做了统一处理,统一返回:参数(传入的fixtures),value(参数数据)的形式
    def parameters_data(self, fixtures, parameters):
        """
        参数化实现2种方式:
        方式1:
        config:
        name: post示例
        fixtures: username, password
        parameters:
            - [test1, '123456']
            - [test2, '123456']
        方式2:
        config:
        name: post示例
        parameters:
            - {"username": "test1", "password": "123456"}
            - {"username": "test2", "password": "1234562"}
        :returns 统一合并:key和value
            fixtures: 用例需要用到的fixtures: ['username', 'password']
            parameters: 参数化的数据list of list : [['test1', '123456'], ['test2', '123456']]
        """
        #1.将fixtures的str换成list
        if isinstance(fixtures, str):
            fixtures = [item.strip(' ') for item in fixtures.split(',')]
        if isinstance(parameters, list) and len(parameters) >= 1:  # 收集方式二中的key和value
            if isinstance(parameters[0], dict):
                params = list(parameters[0].keys())
                new_parameters = []
                for item in parameters:
                    new_parameters.append(list(item.values()))
                for param in params:
                    if param not in fixtures:
                        fixtures.append(param)
                return fixtures, new_parameters
            else:
                return fixtures, parameters
        else:
            # 没有参数化数据,直接返回[]
            fixtures, []

上面增加的代码解决了:1.将参数作为fixtures传入到用例函数中,2.动态生成用例函数前,先增加module中的属性:参数名和参数数据
创建test_x.yml文件

config:
  fixtures: user, psw
  parameters:
    - [a, 'b']
    - [c, d]

test_x1:
  name: 用例1
  parameters:
    - {"username": "test1", "password": "123456"}
    - {"username": "test2", "password": "1234562"}
  print: "1-${user}"

test_x2:
  name: 用例2
  print: "2-${user}"

由参数化数据,可知,该yml文件参数化,生成6条case。运行:pytest .\test_x.yml

标签:case,12,parameters,关键字,config,yaml,params,fixtures,data
From: https://www.cnblogs.com/dack-zt-deng/p/17903887.html

相关文章

  • 2023-12-15
    packagecom.example.backendmanage.controller;importcn.hutool.core.io.IoUtil;importcn.hutool.core.util.StrUtil;importcn.hutool.http.server.HttpServerResponse;importcn.hutool.poi.excel.ExcelReader;importcn.hutool.poi.excel.ExcelUtil;importcn.hu......
  • macOS Monterey 12.7.2 (21G1974) Boot ISO 原版可引导镜像下载
    macOSMonterey12.7.2(21G1974)BootISO原版可引导镜像本站下载的macOS软件包,既可以拖拽到Applications(应用程序)下直接安装,也可以制作启动U盘安装,或者在虚拟机中启动安装。另外也支持在Windows和Linux中创建可引导介质。macOSMonterey12.7.x如无特殊说明,皆为安......
  • Python 异步编程之yield关键字
    背景介绍在前面的篇章中介绍了同步和异步在IO上的对比,从本篇开始探究python中异步的实现方法和原理。python协程的发展流程:python2.5为生成器引用.send()、.throw()、.close()方法python3.3为引入yieldfrom,可以接收返回值,可以使用yieldfrom定义协程Python3.4加入了asy......
  • 文心一言 VS 讯飞星火 VS chatgpt (157)-- 算法导论12.3 4题
    四、用go语言,删除操作可交换吗?可交换的含义是,先删除x再删除y留下的结果树与先除y再删除x留下的结果树完全一样。如果是,说明为什么?否则,给出一个反例。文心一言:在Go语言中,删除操作是不可交换的。这意味着先删除节点x再删除节点y与先删除节点y再删除节点x留下的......
  • 12.15---python文件读取
    withopen('pi_digits.txt')asfile:contents=file.read()print(contents.strip())要想访问文件内容需要先打开它才能访问,函数open()接受一个参数:要打开文件的名称。在当前执行文件的目录中查找文件名。代码中,open('E:/python/文件和异常/pi_digits.txt')返回一个表示......
  • RV1126 快速启动
    一、硬件信息RV1126/RV1109系列芯⽚内置硬件解压缩模块--decom,可以极⼤得提升系统启动速度RV1126/RV1109内置⼀个MCU,MCU在SoC上电后就会快速启动,迅速初始化Camera和ISP,然后尽可能快得保存前⼏帧图像。RV1126支持快速启动的存储介质存储介质类型读取速度......
  • 红帽认证RHCE9.0版本2023年12月的红帽9.0RHCE9版本RHCSA考试题⽬+考试答案,本人已过,全
     translatorAfrikaansAlbanian-shqipeArabic-‎‫العربية‬‎Armenian-ՀայերէնAzerbaijani-azərbaycancaBasque-euskaraBelarusian-беларускаяBengali-বাংলাBulgarian-българскиCatalan-catalàChi......
  • 2023-12-15
    一、公司需求:一个设备接入平台。这里使用开源的thingspanel。二、平台部署:购买Linux服务器,使用Docker容器从官网拉取源码一键部署。三、Docker部署前端程序:1.下载官网Front-end程序源码,VScode安装npm编译生成dist目录。2.用WinSCP工具通过scp上传dist文件到Linux服务器。3......
  • 2023.12.15日报
    今天主要是实现了一个简单的问卷页面,这个页面做来很艰难,因为存在各种各样的依赖版本不兼容的问题另外,还有引用的路径问题,我一开始确实没想到,仅仅因为一个图标引入存在问题就导致整个页面空白这让我对vue有了新的认识,放张图在下边 在左侧可以通过拖动实现添加不同种类的题目,在......
  • 【2023-12-15】理解包容
    11:00负担越重,我们的生命越贴近大地,它就越真切实在。                                                 ——米兰·昆德拉今天下午奶奶要回老家,得提前下班去接手二宝。......