Pytest Fixture夹具参数介绍

Fixture 是一些函数,pytest 会在执行测试函数之前(或之后)加载运行它们。可以用它做一些事情,比如数据库的链接操作之类

import pytest

@pytest.fixture()
def post_code():
    return '010'


def test_postcode(post_code):
    assert post_code == '010'

执行结果

 

预处理和后处理

        很多时候需要在测试前进行预处理(如新建数据库连接),并在测试完成进行清理(关闭数据库连接)。

        当有大量重复的这类操作,最佳实践是使用固件来自动化所有预处理和后处理。

        Pytest 使用 yield 关键词将固件分为两部分,yield 之前的代码属于预处理,会在测试前执行;yield 之后的代码属于后处理,将在测试完成后执行。

以下测试模拟数据库查询,使用 Fixture来模拟数据库的连接关闭:

import pytest


@pytest.fixture()
def db():
    print('Connection success')
    yield
    print('closed')


def search_user(user_id):
    d = {
        '0001': 'lowen'
    }
    return d[user_id]


def test_search(db):
    assert search_user('0001') == 'lowen'

结果成功前后标识前后有数据库的连接和关闭操作

 

scope用法:作用域

固件的作用是为了抽离出重复的工作和方便复用,为了更精细化控制固件(比如只想对数据库访问测试脚本使用自动连接关闭的固件),pytest 使用作用域来进行指定固件的使用范围。

在定义固件时,通过 scope 参数声明作用域,可选项有:

1.function: 函数级,每个测试函数都会执行一次固件;
2.class: 类级别,每个测试类执行一次,所有方法都可以使用;
3.module: 模块级,每个模块执行一次,模块内函数和方法都可使用;
4.session: 会话级,一次测试只执行一次,所有被找到的函数和方法都可用;
5.package: 包级别,每个python包只执行一次;

默认的作用域为 function。 

import pytest

@pytest.fixture(scope='function')
def func_scope():
   print('方法级别')

@pytest.fixture(scope='module')
def mod_scope():
    print('模块级别')


@pytest.fixture(scope='session')
def sess_scope():
    print('会话级别')


@pytest.fixture(scope='class')
def class_scope():
    print('类级别')


def test_multi_scope(sess_scope, mod_scope, func_scope):
    pass

执行结果如下,可以清楚看到各固件的作用域和执行顺序:

 对于类使用作用域,需要使用 pytest.mark.usefixtures (对函数和方法也适用)

import pytest


@pytest.fixture(scope='class')
def class_scope():
    print('类级别前置')
    yield
    print('类级别后置')


@pytest.mark.usefixtures('class_scope')
class TestClassScope:
    def test_1(self):
        print("test_1方法")

    def test_2(self):
        print("test_2方法")

叠加usefixtures

如果一个方法或者一个class用例想要同时调用多个fixture,可以使用@pytest.mark.usefixture()进行叠加。注意叠加顺序,先执行的放底层,后执行的放上层。

import pytest


@pytest.fixture(scope='function')
def class_open():
    print('方法级别前置')
    yield
    print('方法级别后置')


@pytest.fixture(scope='class')
def class_close():
    print('类级别前置')
    yield
    print('类级别后置')


class TestClassScope:
    @pytest.mark.usefixtures('class_open')
    def test_1(self):
        print("test_1方法")

    @pytest.mark.usefixtures('class_open')
    @pytest.mark.usefixtures('class_close')
    def test_2(self):
        print("test_2方法")


if __name__ == '__main__':
    pytest.main(['-vs'])

autouse用法:自动执行

目前为止,所有固件的使用都是手动指定,或者作为参数,或者使用 usefixtures。

如果想让固件自动执行,可以在定义时指定 autouse 参数。

下面是两个自动计时固件,一个用于统计每个函数运行时间(function 作用域),一个用于计算测试总耗时(session 作用域)

注意下面的测试函数并都没有使用固件:

import pytest


@pytest.fixture(scope='session', autouse=True)
def timer_session_scope():
    print("用例执行前")
    yield
    print("用例执行后")


def test_one():
    print('test_one方法')

结果如下

我们可以看到,我们选择自动执行,即使我们没有选择使用,pytest也会给自动执行的。执行到对应的function级别。

name用法: name参数表示可以对fixture的名称进行重命名:

注意:通过name重命名后,继续使用以前的名字调用会报错。

import pytest


@pytest.fixture(name='anjing')
def login():
    print('\n登录操作')
    yield
    print('\n退出登录!')


class TestLogin:
    def test_01(self, anjing):
        print('---用例01---')

    def test_02(self):
        print('---用例02---')


if __name__ == '__main__':
    pytest.main(['-vs'])
ids用法:ids表示在fixture对参数化的内容进行加上标识,比如让别人知道这个传入的参数是什么意思。用于什么样的测试用例。默认是传参数内容
import pytest

data = ['anjing', 'test', 'admin']


@pytest.fixture(params=data, ids=['user=anjing', 'user=test', 'user=admin'])
def login(request):
    print('登录功能')
    yield request.param
    print('退出登录')


class TestCase:
    def test_01(self, login):
        print('---用例01---')
        print(f'登录的用户名{login}')

    def test_02(self):
        print('---用例02---')


if __name__ == '__main__':
    pytest.main(['-vs'])

params用法:参数化

  params表示fixture的参数化功能。这里会有一个request参数,主要用来接收fixture的返回结果。并通过request.param返回结果内容。

        假设现在有一批 API 需要测试对不同数据库的支持情况(对所有数据库进行相同操作),最简单的方法就是针对每个数据库编写一个测试用例,但这包含大量重复代码,如数据库的连接、关闭,查询等。

        使用固件抽离出数据库的通用操作,每个 API 都能复用这些数据库固件,同时可维护性也得到提升。

import pytest


@pytest.fixture(params=[
    ('redis', '6379'),
    ('mysql', '6300')
])
def db_config(request):
    print(f"数据库的配置:{request.param}")
    return request.param


@pytest.fixture(autouse=True)
def db(db_config):
    print(f'数据库连接了{db_config}')
    yield
    print(f'数据库关闭了{db_config}')


def test_api():
    assert 1 == 1

执行结果:

 

pytest测试用例参数化 @pytest.fixture 与 @pytest.mark.parametrize 结合使用【用例参数化传入参数设置:indirect=True】

如果测试数据需要在 fixture 方法中使用,同时也需要在用例中使用,可以让 parametrize  的  indirect  参数为  True  

当设置indirect = True时,pytest 会把 argnames 当做函数执行,将 argvalues 作为参数传入到 argnames 函数中

 

import pytest


test_user_data = ['Tom', 'Lowen']

# 方法名作为参数
@pytest.fixture(scope='module')
def login(request):
    # 通过 request.param 获取参数
    user = request.param
    print(f"登录用户: {user}")
    return user


@pytest.mark.parametrize("login", test_user_data, indirect=True)
def test_login(login):
    a = login
    print(f"用例中 login 的返回值; {a}")
    assert a != None

 

夹具 yield和return的区别

夹具中可以使用return,yield关键字为测试函数提供值,推荐使用yield关键字,他们的区别如下:

  • yield返回值后,后面的代码还会继续运行
  • return返回值后,后面的代码不会继续运行

  

posted @ 2022-03-30 17:01  北京测试菜鸟  阅读(245)  评论(0)    收藏  举报