【pytest】(十)fixture参数化-巧用params和ids优雅的创建测试数据

简介: 【pytest】(十)fixture参数化-巧用params和ids优雅的创建测试数据

我们都知道参数化。


比如我要测试一个查询接口/test/get_goods_list,这个接口可以查询到商品的信息。


在请求中,我可以根据请参数goods_status的不同传值,可以查询到对应状态的商品数据,比如:1-未销售2-销售中3-已售罄


那么在编写自动化测试case的时候,在断言里就要分别验证到这3种状态的商品数据。

通常,在执行case之前,会去数据库分别插入对应状态的商品数据,来满足测试需求。

而在pytest框架中,我喜欢用fixture()去实现测试数据的准备和清理工作。


于是,2种实现方式瞬间出现:


  • 写3个case,只有传参不一样。对应写3个fixture来分别初始化3种状态的商品数据。
  • 写1个case,在case里用@pytest.mark.parametrize()进行参数化。只写一个fixture一次性的插入3种状态的商品数据。


在这2个方法里,显然第二种更优雅,避免了case的冗余代码。


但是一把梭的插入所有的测试数据还是差点意思,如果我只想执行其中的某一个数据的case,那么其他2个不必要的数据也生成了。


所以,我想要的样子是,可以自由的根据执行的case的参数,去对应的初始化测试数据。


具体点的描述就是:参数化里3个参数,我只执行2-销售中的时候,只去插入2-销售中这一种状态的数据。


网上搜的都是简单的fixture参数化的东西,达不到我想要的需求。于是乎我自己去翻阅官方文档,发现可以用fixture中的params和ids

这2个参数去实现我的需求。


一、fixture中的params


params是一个列表,用来存放我们要参数化的值。


举例:这里的代码放了参数1参数2 这2个参数,2个测试case,都会用params里的参数去分别执行2次。


import pytest
@pytest.fixture(params=['参数1', '参数2'])
def my_fixture(request):
    return request.param
def test_fixtures_01(my_fixture):
    print('\n 执行test_fixtures_01')
    print(my_fixture)
def test_fixtures_02(my_fixture):
    print('\n 执行test_fixtures_02')
    print(my_fixture)


运行一下:


collecting ... collected 4 items
test_ids.py::test_fixtures_01[\u53c2\u65701] PASSED                      [ 25%]
 执行test_fixtures_01
参数1
test_ids.py::test_fixtures_01[\u53c2\u65702] PASSED                      [ 50%]
 执行test_fixtures_01
参数2
test_ids.py::test_fixtures_02[\u53c2\u65701] PASSED                      [ 75%]
 执行test_fixtures_02
参数1
test_ids.py::test_fixtures_02[\u53c2\u65702] PASSED                      [100%]
 执行test_fixtures_02
参数2
============================== 4 passed in 0.03s ==============================
Process finished with exit code 0


二、fixture中的ids


ids也是要结合着params一起使用的。当有多个 params 时,针对每一个 param,可以指定一个id


然后,这个 id 会变成测试用例名字的一部分。如果没有提供 id,则 id 将自动生成。


import pytest
@pytest.fixture(params=['参数1', '参数2'], ids=["id-01", "id-02"])
def my_fixture(request):
    return request.param
def test_fixtures_01(my_fixture):
    print('\n 执行test_fixtures_01')
    print(my_fixture)


运行下,结果里case名称后分别带了 id:[id-01][id-02]


collecting ... collected 2 items
test_ids.py::test_fixtures_01[id-01] PASSED                              [ 50%]
 执行test_fixtures_01
参数1
test_ids.py::test_fixtures_01[id-02] PASSED                              [100%]
 执行test_fixtures_01
参数2
============================== 2 passed in 0.02s ==============================
Process finished with exit code 0


三、利用ids实现需求


ids的赋值除了上述的方式之外,还有一种,也就是帮我实现需求的一种,直接看代码。


import pytest
def init_data(fixture_value):
    if fixture_value == 1:
        return "unsold"
    elif fixture_value == 2:
        return "onSale"
    elif fixture_value == 3:
        return "sellOut"
@pytest.fixture(params=[1, 2, 3], ids=init_data)
def my_method(request):
    req_param = request.param
    print("\n参数为:【{}】,执行sql--插入【{}】状态的数据".format(req_param, req_param))
    yield req_param
    print("\n执行sql--清理参数为【{}】的测试数据".format(req_param, req_param))
    print("\n----------------------------------------")
def test_01(my_method):
    print("\n正在执行【{}】的case--".format(my_method))
if __name__ == '__main__':
    pytest.main(['-s', '-v',  'test_my_fixture.py::test_01'])


示例代码就没有去真正的写一个接口的case了,因为太(lan)忙了,所以直接用print()打印出我要做的事儿。


上述代码中,重点就是3个部分:


  • test_01(),这是测试case
  • my_method()这是我定义的fixture函数
  • init_data()这个是用来初始化测试数据的函数


1、test_01()


测试case没什么说的,一般来说接口的case里的组成就是:传参调用测试接口断言


case里传入了my_method函数,这是调用fixture的一种方式。


def test_01(my_method):
    print("\n正在执行[{}]的case--".format(my_method))


2、my_method()


我定义了my_method这个fixture去进行case执行之前的测试数据处理。


@pytest.fixture(params=[1, 2, 3], ids=init_data)
def my_method(request):
    req_param = request.param
    print("\n参数为:{}".format(req_param))
    yield req_param
    print("\n执行sql--清理参数为【{}】的测试数据".format(req_param))
    print("\n----------------------------------------")


params=[1,2,3]就是相当于我接口请求体里查询不同状态商品的数据对应的参数,1-未销售2-销售中3-已售罄


ids在上面单独介绍的例子中是直接赋值的,但是在这里,我是把一个函数赋给了它,这个函数就是init_data()


当然了,为了满足后面我的指定参数执行case的需求,init_data要返回具体的id才行。


3、init_data()


init_data(fixture_value)函数里传入的fixture_value,其实就是fixture函数里的params=[1, 2, 3]


return出来的则是这个参数对应的id,分别是"未销售""销售中""已售罄",那么我就可以通过pytest命令 加上 -k来指定要运行的case。


def init_data(fixture_value):
    if fixture_value == 1:
        return "unsold"
    elif fixture_value == 2:
        return "onSale"
    elif fixture_value == 3:
        return "sellOut"


先不用-k,看下整个的运行结果。


if __name__ == '__main__':
    pytest.main(['-s', '-v', 'test_my_fixture.py::test_01'])


运行结果:


collected 3 items                                                                                                                                                            
test_my_fixture.py::test_01[\u672a\u9500\u552e]
参数为:【1】,执行sql--插入【1】状态的数据
正在执行【1】的case--
PASSED
执行sql--清理参数为【1】的测试数据
----------------------------------------
test_my_fixture.py::test_01[\u9500\u552e\u4e2d]
参数为:【2】,执行sql--插入【2】状态的数据
正在执行【2】的case--
PASSED
执行sql--清理参数为【2】的测试数据
----------------------------------------
test_my_fixture.py::test_01[\u5df2\u552e\u7f44]
参数为:【3】,执行sql--插入【3】状态的数据
正在执行【3】的case--
PASSED
执行sql--清理参数为【3】的测试数据
----------------------------------------
============================================================================= 3 passed in 0.03s =============================================================================


可以看到,在case执行之前,就插入了3种状态的测试数据,并且运行了3个case。


接下来,我们用-k来执行 id是"onSale"的case:


if __name__ == '__main__':
    pytest.main(['-s', '-v', '-k', "onSale", 'test_my_fixture.py::test_01'])


运行结果:


collected 3 items / 2 deselected / 1 selected                                                                                                                                
test_my_fixture.py::test_01[onSale]
参数为:【2】,执行sql--插入【2】状态的数据
正在执行【2】的case--
PASSED
执行sql--清理参数为【2】的测试数据
----------------------------------------
====================================================================== 1 passed, 2 deselected in 0.03s ======================================================================


可以看到,找到了3个case,但是只执行了我们制定要运行的case。


在我以前写的case中,其实并没有这样写。之前我们写了一个mock服务,于是乎我就把一些会变化的请求参数也配置进去了,然后根据我

传参的不同,去拿到我想要的请求body,最后再去请求我要测试的接口。


现在新换了个地方,每天忙于dian业dian务dian,工作中突然有了这个想法,于是乎抽空找寻了下方法。这个方法我在后面的自动化服务搭建

中会去运用,届时有新想法再分享。

相关文章
|
8天前
|
分布式计算 Shell MaxCompute
odps测试表及大量数据构建测试
odps测试表及大量数据构建测试
|
8天前
|
存储 测试技术 API
pytest接口自动化测试框架搭建
通过上述步骤,我们成功搭建了一个基于 `pytest`的接口自动化测试框架。这个框架具备良好的扩展性和可维护性,能够高效地管理和执行API测试。通过封装HTTP请求逻辑、使用 `conftest.py`定义共享资源和前置条件,并利用 `pytest.ini`进行配置管理,可以大幅提高测试的自动化程度和执行效率。希望本文能为您的测试工作提供实用的指导和帮助。
50 15
|
14天前
|
开发框架 .NET Java
C#集合数据去重的5种方式及其性能对比测试分析
C#集合数据去重的5种方式及其性能对比测试分析
31 11
|
15天前
|
开发框架 .NET Java
C#集合数据去重的5种方式及其性能对比测试分析
C#集合数据去重的5种方式及其性能对比测试分析
41 10
|
2月前
|
机器学习/深度学习 算法 UED
在数据驱动时代,A/B 测试成为评估机器学习项目不同方案效果的重要方法
在数据驱动时代,A/B 测试成为评估机器学习项目不同方案效果的重要方法。本文介绍 A/B 测试的基本概念、步骤及其在模型评估、算法改进、特征选择和用户体验优化中的应用,同时提供 Python 实现示例,强调其在确保项目性能和用户体验方面的关键作用。
45 6
|
2月前
|
机器学习/深度学习 算法 UED
在数据驱动时代,A/B 测试成为评估机器学习项目效果的重要手段
在数据驱动时代,A/B 测试成为评估机器学习项目效果的重要手段。本文介绍了 A/B 测试的基本概念、步骤及其在模型评估、算法改进、特征选择和用户体验优化中的应用,强调了样本量、随机性和时间因素的重要性,并展示了 Python 在 A/B 测试中的具体应用实例。
33 1
|
3月前
|
测试技术
自动化测试项目学习笔记(五):Pytest结合allure生成测试报告以及重构项目
本文介绍了如何使用Pytest和Allure生成自动化测试报告。通过安装allure-pytest和配置环境,可以生成包含用例描述、步骤、等级等详细信息的美观报告。文章还提供了代码示例和运行指南,以及重构项目时的注意事项。
350 1
自动化测试项目学习笔记(五):Pytest结合allure生成测试报告以及重构项目
|
3月前
|
存储 测试技术 数据库
数据驱动测试和关键词驱动测试的区别
数据驱动测试 数据驱动测试或 DDT 也被称为参数化测试。
46 1
|
3月前
|
测试技术 Python
自动化测试项目学习笔记(四):Pytest介绍和使用
本文是关于自动化测试框架Pytest的介绍和使用。Pytest是一个功能丰富的Python测试工具,支持参数化、多种测试类型,并拥有众多第三方插件。文章讲解了Pytest的编写规则、命令行参数、执行测试、参数化处理以及如何使用fixture实现测试用例间的调用。此外,还提供了pytest.ini配置文件示例。
73 2
|
3月前
|
机器学习/深度学习 监控 计算机视觉
目标检测实战(八): 使用YOLOv7完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)
本文介绍了如何使用YOLOv7进行目标检测,包括环境搭建、数据集准备、模型训练、验证、测试以及常见错误的解决方法。YOLOv7以其高效性能和准确率在目标检测领域受到关注,适用于自动驾驶、安防监控等场景。文中提供了源码和论文链接,以及详细的步骤说明,适合深度学习实践者参考。
730 0
目标检测实战(八): 使用YOLOv7完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)