pytest使用(3)-conftest

接着上一节的fixture,这一节继续介绍conftest.

我们知道在python中要想实现数据的共享,可以定义一个“全局变量”,在另一个文件中使用的时候通过先导入,再使用的方式来访问。pytest中定义个conftest.py来实现数据,参数,方法、函数的共享。
conftest.py 的文件名称是固定的, pytest 会自动识别该文件,我们可以理解成一个专门存放 fixture 的配置文件。一个工程下可以建多个 conftest.py 文件,一般我们都是在工程根目录下设置的 conftest 文件,这样会起到一个全局的作用。 我们也可以在不同的子目录下放 conftest.py ,这样作用范围只能在该层级的子目录下生效。

总而言之:conftest.py文件是Pytest特有配置文件,只能用来做如下三个功能:

  1. 设置项目和fixture
  2. 导入外部插件
  3. 指定钩子函数

conftest特点

1、conftest.py可以跨.py文件调用,有多个.py文件调用时,可让conftest.py只调用了一次fixture,或调用多次fixture
2、conftest.py与运行的用例要在同一个pakage下,并且有init.py文件
3、不需要import导入 conftest.py,pytest用例会自动识别该文件,放到项目的根目录下就可以全局目录调用了,如果放到某个package下,那就在改package内有效,可有多个conftest.py
4、conftest.py配置脚本名称是固定的,不能改名称
5、conftest.py文件不能被其他文件导入
6、所有同目录测试文件运行前都会执行conftest.py文件

在上一章节中结识了fixture。fixture里面有个scope参数可以控制fixture的作用范围:session>module>class>function

function:每一个函数或方法都会调用
class:每一个类调用一次,一个类中可以有多个方法
module:每一个.py文件调用一次,该文件内又有多个function和class
session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

conftest结合fixture使用

scope参数为session:所有测试.py文件执行前执行一次
scope参数为module:每一个测试.py文件执行前都会执行一次conftest文件中的fixture
scope参数为class:每一个测试文件中的测试类执行前都会执行一次conftest文件中的
scope参数为function:所有文件的测试用例执行前都会执行一次conftest文件中的fixture

conftest使用场景

fixture适用于在同一个py文件中多个用例执行时的使用;而conftest.py方式适用于多个py文件之间的数据共享。比如常见的有以下场景:

  • 请求接口需要共享登录接口的token/session
  • 多个case共享一套测试数据
  • 多个case共享配置信息

conftest示例

# conftest.py

import pytest
@pytest.fixture(scope='session')
def get_token():
    token = 'qeehfjejwjwjej11sss@22'
    print('conftest中輸出token:%s' % token)
    return token
# test_02.py
import pytest

class Test(object):
    def test2(self, get_token):
        token = 'qeehfjejwjwjej11sss@22'
        print("【执行test02.py-Test类-test2用例,获取get_token:%s】" % get_token)
        assert get_token == token


if __name__ == "__main__":
    pytest.main(["-s", "test_02.py", "test_03.py"])
# test_03.py
import pytest

class Test(object):
    def test3(self, get_token):
        token = 'qeehfjejwjwjej11sss@22'
        print("【执行test03.py-Test类-test3用例,获取get_token:%s】" % get_token)
        assert get_token == token

    def test4(self, get_token):
        token = 'qeehfjejwjwjej11sss@22'
        print("【执行test03.py-Test类-test4用例,获取get_token:%s】" % get_token)
        assert get_token == token

文件目录层级如下所示
在这里插入图片描述
打开testreports.html
当conftest.py中的fixture(scope=session)时,所有的测试py文件执行前执行一次
在这里插入图片描述
当conftest.py中的fixture(scope=module)时,每一个测试.py文件执行前都会执行一次conftest文件中的fixture
在这里插入图片描述
当conftest.py中的fixture(scope=class)时,每一个测试文件中的测试类执行前都会执行一次conftest文件中的
在这里插入图片描述
当conftest.py中的fixture(scope=function)时,所有文件的测试用例执行前都会执行一次conftest文件中的fixture
在这里插入图片描述

yield实现teardown

每个测试用例完成后,应该做好资源回收,此时就需要使用到 teardown函数的善后工作了。用 fixture 实现 teardown 并不是一个独立的函数,而是用 yield 关键字来开启 teardown 操作。

当 pytest.fixture(scope=“session”) 时,作用域是整个测试会话,即开始执行pytest 到结束测试只会执行一次。
当 pytest.fixture(scope=“module”) 时, module 作用是整个 .py 文件都会生效(整个文件只会执行一次),用例调用时,参数写上函数名称就可以。
当 pytest.fixture(scope=“class”) 时,每一个测试文件中的测试类执行前都会执行一次conftest文件中的
当 pytest.fixture(scope=“function”) 时,pytest 的 yield 类似 unittest 的 teardown 。每个方法(函数)都会执行一次。

修改conftest.py文件

# conftest.py

import pytest
@pytest.fixture(scope='session')
def get_token():
    token = 'qeehfjejwjwjej11sss@22'
    print('conftest中开始输出token:%s' % token)
    yield token
    print('conftest中结束输出token:%s' % token)

test02.py修改如下所示:

# test_02.py
import pytest

class Test(object):
    def test2(self, get_token):
        token = 'qeehfjejwjwjej11sss@22'
        print("【执行test02.py-Test类-test2用例,获取get_token:%s】" % get_token)
        assert get_token == token
class Test01(object):
    def test_01(self, get_token):
        token = 'qeehfjejwjwjej11sss@22'
        print("【执行test02.py-Test类-test2用例,获取get_token:%s】" % get_token)
        assert get_token == token

test03.py文件不变。执行输出命令
在这里插入图片描述
scope=session时
在这里插入图片描述
scope=module时,每个文件开始调用conftest初始化,py执行完成后,调用teardown回收
在这里插入图片描述
scope=class时,每个类开始时调用conftest初始化,类中的用例执行完成后,调用teardown完成数据回收
在这里插入图片描述
scope=function时
在这里插入图片描述

addfinalizer实现回收

除了 yield 可以实现 teardown ,在 request-context 对象中注册 addfinalizer 方法也可以实现终结函数。在用法上, addfinalizer 跟 yield 是不同的,需要你去注册作为终结器使用的函数。例如:增加一个函数 myteardown*,并且注册成终结函数。

# conftest.py

import pytest
@pytest.fixture(scope="session")
def get_token(request):
    token = 'qeehfjejwjwjej11sss@22'
    print('conftest中开始输出token:%s' % token)
    # yield token
    def myteardown1():
        print('conftest中结束1输出token:%s' % token)
    def myteardown2():
        print('conftest中结束2输出token:%s' % token)

    request.addfinalizer(myteardown1)
    request.addfinalizer(myteardown2)

    return token
    

在这里插入图片描述

参数传递

参数传递有两个方向,一个是case给conftest.py传递参数,另一个是case中pytest.mark.parametrize给用例传递参数,下面介绍一下常用的参数传递方式。

parametrize向下给case传递参数

@pytest.mark.parametrize('policy', ['REJECT', 'CONTINUE'])
def test_create_func(policy):
    res, code = obj.openapi_create_func(ploicy=ploicy,
                                                id=***,
                                                )

这种是向下传递参数,该case会执行两个场景,分别是policy中的两个参数

parametrize向上给conftest传递参数

对于有些场景,需要将部分内容提炼到conftest.py中。同时指定conftest中fixture函数的scope范围已经是否autouse。如弹性编排的自动化测试用例,对于弹性伸缩下的其他资源来说,伸缩组和伸缩配置相关信息可以提取放在conftest.py中,因为这个是弹性编排的基础部分。项目结构如下
在这里插入图片描述

为方便介绍,后文中第一个conftest称之为conftest1,类似第二个conftest称之为conftest2。

#conftest1底层的结构
import pytest


@pytest.fixture()
def preparefunc(request, as_client, *args):
    cooldown = desirenum = minnum = maxnum = 0
    if hasattr(request, 'param'):
        cooldown = request.param.get('cooldown')
        desirenum = request.param.get('desirenum')
        minnum = request.param.get('minnum')
        maxnum = request.param.get('maxnum')

    result, status_code = as_client.open_api_create_func(cooldown=cooldown,
                                                         desirenum=desirenum,
                                                         minnum=minnum,
                                                         maxnum=maxnum)
    assert status_code == 200
    return result
    
    def teardown():
        ...
    
    request.addfinalizer(teardown)

@pytest.fixture()
def vm(request, as_client, *args):
    ...

如果prepareas,在用例不传递参数则使用默认参数,如果传递参数则使用传递的参数给conftest中的fixture函数。在用例层

#test_scaleout_csas.py
import allure
import pytest


class TestLifycycleHook():
    @allure.title(f'case使用conftest中的默认参数')
    def test_scaleout_policy001(self, prepareas):
        ...
    

    @allure.title(f'parametrize给conftest中的一个fixture函数传递参数并将conftest返回值作为case的参数进行使用')
    @pytest.mark.parametrize('prepareas', [{'desirenum': "1",
                                            'minnum': '2'}], indirect=True)
    def test_scaleout_policy002(self, prepareas):
        ...


    @allure.title(f'parametrize给conftest中的两个个fixture函数传递参数并将conftest返回值作为case的参数进行使用')
    @pytest.mark.parametrize('prepareas, vm', [{'count': '1', 'charType': 'prepaid'},
                                               {'desirenum': "1", 'minnum': '2'}],                               
                                               indirect=True)
    def test_scaleout_policy003(self, prepareas):
        ...

在调用vm的创建ECS虚拟机的时候,有时需要同时指定系统盘和数据盘。但是创建云盘的时候,系统盘和数据盘的key是一样的,此时如果传递的是如下格式

@pytest.mark.parametrize('vm', [{'kind':'system'}, {'kind':'data'}], indirect=True)
def test_vm(vm):
...

则被pytest框架认为是创建两个ECS虚拟机,而不是一个云盘类型同时包括系统盘和数据盘

parametrize向上给conftest传递相同key的多个数据

此时可以使用namedtuple将如上数据封装成一个对象。当然也可以直接将两个数据使用tuple封装后然后在conftest中的vm解封装也可以。这里介绍下使用namedtuple

# test_create_vm

import pytest
from collections import namedtuple

func = namedtuple('VM', ['name', 'system', 'data'])
data = func('functest', {'kind':'system'}, {'kind':'data'})
@pytest.mark.parametrize('vm', [vmdata,], indirect=Ture)
def test_case(vm):
...
# conftest.py

@pytest.fixture()
def vm(request):
...

接着上文,返回到项目工程目录,在lifecyclehooktest下还有一个conftest用于封装和life*相关的内容。这里conftest2如下:

#conftest2
import pytest

@pytest.fixture()
def createfunc(request, as_client, prepareas):
    res, code = as_client.open_api_create_func(id=prepareas, name='zhiyu')
    assert code == 200
    return res

因为conftest2中的创建依赖conftest1中的 prepareas。在测试lifechclye中希望给prepareas传递参数后将其返回值用于conftest2创建createlifecyclehook,并将其返回给测试用例中来。这个时候需要使用到lazy_fixture。pytest-lazy-fixture 插件,解决在测试用例中使用 @pytest.mark.parametrize 参数化时调用 fixture。

pytest-lazy-fixture

需求是如下:test_scaleout_case中使用到了创建hooks,因此使用到conftest2,conftest2需要指定伸缩组中desireNumber的数量并使用scalinggroup,因此使用到conftest1。
在这里插入图片描述
参考:pytest-lazy-fixture

  • 5
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值