pytest---fixture运行规则

1,fixture是依赖注入的一种形式

2,fixture的发现先后规则为:测试类,测试模块,coftest,内置,第三方

  多个fixture的运行先后顺序:作用范围越高,越先运行,autouse的优先级高于非autouse的,有依赖的fixture先运行依赖的fixtue,按照注入的先后顺序执行 

import pytest

# fixtures documentation order example
order = []


@pytest.fixture(scope="session")
def s1():
    order.append("s1")


@pytest.fixture(scope="module")
def m1():
    order.append("m1")


@pytest.fixture
def f1(f3):
    order.append("f1")


@pytest.fixture
def f3():
    order.append("f3")


@pytest.fixture(autouse=True)
def a1():
    order.append("a1")


@pytest.fixture
def f2():
    order.append("f2")


def test_order(f1, m1, f2, s1):
    assert order == ["s1", "m1", "a1", "f3", "f1", "f2"]

3,对于需要花费比较长时间的操作,放到fixture中并且使用scope参数添加范围,避免多次加载花费大量时间

4, fixture的范围

  function,class,model,package,seesion

  可以动态来定义fixture的范围

# 通过命令行的参数来控制fixture的范围
def
determine_scope(fixture_name, config): if config.getoption("--keep-containers", None): return "session" return "function" @pytest.fixture(scope=determine_scope) def docker_container(): yield spawn_containe

5,fixture添加结束代码的方式

    1,通过yield关键字来做类似teardown的方法

    2,通过request.addfinalizer(方法名)的方式

def test_end():
    logging.info('结束测试')
    
@pytest.fixture(scope='session',autouse=True)
def tear_down_py(request):
    a=2
    logging.info('开始')
    request.addfinalizer(test_end)  # 这里的参数为方法名
    return a

6,测试用例中传递一些值到fixture中,然后fixture对这些数据进入处理后返回处理结果的方法

   使用request.node.get_closest_marker和@pytest.mark的方法来做

@pytest.fixture
def fixt(request):
    marker = request.node.get_closest_marker("fixt_data")
    if marker is None:
        # Handle missing marker in some way...
        data = None
    else:
        data = marker.args[0]

    # 对数据做处理
    data = data+1
    return data


class TestLoginSuccess:

    @pytest.mark.fixt_data(42)
    def test_fixt(self, fixt):
        assert fixt == 42

7,fixture也可以返回一个函数,在测试用例中多次调用这个fixture

  

@pytest.fixture
def get_name_func():
    # 用闭包的形式使fixture返回一个函数,然后在测试用例中可以多次调用
    def _get_name(name):

        return {'name':name, 'age': 12}

    return _get_name


class TestLoginSuccess:

    def test_fixt(self, get_name_func):
        assert get_name_func('张三')['name'] == '张三'
        assert get_name_func('张四')['name'] == '张四'
        assert 0

  同样也可以使用yield关键字来对之前创造的一些数据进入teardown的处理

  

@pytest.fixture
def get_name_func():
    # 用闭包的形式使fixture返回一个函数,然后在测试用例中可以多次调用

    creat_list = []

    def _get_name(name):
        creat_list.append(name)
        logging.info(creat_list)
        return {'name':name, 'age': 12}
    
    yield _get_name
    # 对创建对数据进行处理
    creat_list=[]
    logging.info(creat_list)

class TestLoginSuccess:

    def test_fixt(self, get_name_func):
        assert get_name_func('张三')['name'] == '张三'
        assert get_name_func('张四')['name'] == '张四'
        assert 0

  

8, 参数化数据

  -k 运行id包含指定关键字的用例,  --collect-only:只展示所有要运行用例,不实际运行,并且展示出参数化数据的id

@pytest.fixture(params=['123','456'],ids=['first','second'])
def para_func(request):
    return request.param

class TestLoginSuccess:

    def test_fixt(self, para_func):
        assert para_func == '123'

  对id进行动态生成

def change_id(param):
    if '1' in param:
        return 'first'
    else:
        return 'second'


@pytest.fixture(params=['123','456'],ids=change_id)
def para_func(request):
    return request.param


class TestLoginSuccess:
    def test_fixt(self, para_func):
        assert para_func == '123'

  对某个参数添加mark标记,在params中使用 pytest.param('789', marks=pytest.mark.skip),,

  注意这个和 pytest-html插件一起使用的时候会报一些内容错误,但依然可以使用

@pytest.fixture(params=['123', '456', pytest.param('789', marks=pytest.mark.skip)])
def para_func(request):
    return request.param


class TestLoginSuccess:
    def test_fixt(self, para_func):
        assert para_func == '123'

  

9,fixture之间可以互相使用,但是低作用范围的fixture不能调用高作用范围的fixture

10,当参数化的fixture作用范围大于等于module时,pytest会自动按照参数进行分组,每一个参数只会初始化一次和结束一次,这样有可能会打乱正常的用例执行顺序

  这样是为了减少活动fixture的数量,

@pytest.fixture(scope='module', params=['13', '456'])
def para_func(request):
    print('开始')
    yield request.param
    print('结束')


class TestLoginSuccess:
    def test_01(self, para_func):
        print('执行1')
        assert para_func == '123'

    def test_02(self, para_func):
        print('执行2')

        assert para_func == '123'

##############结果,首先会拿第一个参数进行初始化然后去该作用范围内去找是否有使用该参数的用例,有就执行该参数的用例,然后不结束,再在该范围内找下一个使用这个参数的用例,直到没有了,才执行结束。
          然后下一个参数进行初始化,再继续找。

开始
执行1
F执行2
F结束
开始
执行1
F执行2
F结束
F

  

11,使fixture在测试类,测试moduel,整个项目中生效,需要使用pytest.mark.usefixtures(“fixture名称”)的方式,注意:这个方法用在fixture上无用

@pytest.fixture(scope='module', params=['13', '456'])
def para_func(request):
    print('开始')
    yield request.param
    print('结束')

pytestmark = pytest.mark.usefixtures("para_func")   # 使fixture在整个module中生效,必须使用pytestmark 变量接收

# @pytest.mark.usefixtures("para_func")     # 使fixture在整个class中生效
class TestLoginSuccess:
    def test_01(self):
        print('执行1')
        assert para_func == '123'

    def test_02(self):
        print('执行2')
        assert para_func == '123'

######写在ini文件中,使fixture在整个项目中都生效
# content of pytest.ini
[pytest]
usefixtures = cleandir

  

12,autouse参数谨慎使用,如果使用,尽量在配置/ini文件增加一个配置信息,来控制开启和关闭,避免资源的浪费

  

13,fixture是可以被覆盖的,使用相同的名称,后面的fixture会覆盖前面的fixture,  

6,后续  

      

原文地址:https://www.cnblogs.com/myy-py/p/13516684.html