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

咱们都知道参数化。sql

好比我要测试一个查询接口/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个参数去实现个人需求。code

1、fixture中的params

params是一个列表,用来存放咱们要参数化的值。
举例:这里的代码放了参数1参数2 这2个参数,2个测试case,都会用params里的参数去分别执行2次。orm

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

2、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]get

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

3、利用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()这个是用来初始化测试数据的函数

一、test_01()

测试case没什么说的,通常来讲接口的case里的组成就是:传参调用测试接口断言
case里传入了my_method函数,这是调用fixture的一种方式。

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

二、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才行。

三、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,工做中忽然有了这个想法,因而乎抽空找寻了下方法。这个方法我在后面的自动化服务搭建 中会去运用,届时有新想法再分享。

相关文章
相关标签/搜索