pytest学习和使用6-fixture如何使用?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytest学习和使用6-fixture如何使用?相关的知识,希望对你有一定的参考价值。

(6-fixture如何使用?)

1 引入

  • 和setup、teardown的区别是:fixture可自定义测试用例的前置条件;
  • setup、teardown针对整个脚本全局生效,可实现在执行用例前后加入一些操作;
  • setup、teardown不能做到灵活使用,比如用例A先登陆,用例B不需要登陆,用例C需要登陆,这样使用fixture更容易实现功能。

2 fixture参数说明

2.1 fixture源码

  • 部分源码如下:
def fixture(
    fixture_function: Optional[_FixtureFunction] = None,
    *,
    scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
    params: Optional[Iterable[object]] = None,
    autouse: bool = False,
    ids: Optional[
        Union[
            Iterable[Union[None, str, float, int, bool]],
            Callable[[Any], Optional[object]],
        ]
    ] = None,
    name: Optional[str] = None,
) -> Union[FixtureFunctionMarker, _FixtureFunction]:
  • 我们可看到有五个参数scopeparamsautouseidsname,源码中也对着几个参数进行了说明,如下:
"""Decorator to mark a fixture factory function.

    This decorator can be used, with or without parameters, to define a
    fixture function.

    The name of the fixture function can later be referenced to cause its
    invocation ahead of running tests: test modules or classes can use the
    ``pytest.mark.usefixtures(fixturename)`` marker.

    Test functions can directly use fixture names as input arguments in which
    case the fixture instance returned from the fixture function will be
    injected.

    Fixtures can provide their values to test functions using ``return`` or
    ``yield`` statements. When using ``yield`` the code block after the
    ``yield`` statement is executed as teardown code regardless of the test
    outcome, and must yield exactly once.

    :param scope:
        The scope for which this fixture is shared; one of ``"function"``
        (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``.

        This parameter may also be a callable which receives ``(fixture_name, config)``
        as parameters, and must return a ``str`` with one of the values mentioned above.

        See :ref:`dynamic scope` in the docs for more information.

    :param params:
        An optional list of parameters which will cause multiple invocations
        of the fixture function and all of the tests using it. The current
        parameter is available in ``request.param``.

    :param autouse:
        If True, the fixture func is activated for all tests that can see it.
        If False (the default), an explicit reference is needed to activate
        the fixture.

    :param ids:
        List of string ids each corresponding to the params so that they are
        part of the test id. If no ids are provided they will be generated
        automatically from the params.

    :param name:
        The name of the fixture. This defaults to the name of the decorated
        function. If a fixture is used in the same module in which it is
        defined, the function name of the fixture will be shadowed by the
        function arg that requests the fixture; one way to resolve this is to
        name the decorated function ``fixture_<fixturename>`` and then use
        ``@pytest.fixture(name=<fixturename>)``.
    """

2.2 参数说明

  • 从以上部分源码以及说明我们可以看到:
fixture(scope="function", params=None, autouse=False, ids=None, name=None):
参数 说明
scope 默认:function,还有class、module、package、session
autouse 默认:False,手动调用该fixture;为True,所有作用域内的测试用例都会自动调用该fixture
params 一个可选的参数列表
ids 每个字符串id的列表
name fixture的名称, 默认为装饰函数的名称,同一模块的fixture相互调用建议写个不同的name

3 fixture的特点

  • 命名方式灵活,不局限于 setup 和teardown 这几个命名
  • conftest.py 配置里可以实现数据共享,不需要 import 就能自动找到fixture
  • scope="module" 可以实现多个.py 跨文件共享前置
  • scope="session" 以实现多个.py 跨文件使用一个 session 来完成多个用例

4 fixture如何使用?

4.1 调用方式

4.1.1 方式一:直接传参

# -*- coding:utf-8 -*-
# 作者:NoamaNelson
# 日期:2022/11/17 
# 文件名称:test_mfixture.py
# 作用:fixture的使用
# 联系:VX(NoamaNelson)
# 博客:https://blog.csdn.net/NoamaNelson

import pytest

# 不带参数时默认scope="function"
@pytest.fixture
def case():
    print("这个是登陆功能!")

def test_one(case):
    print("用例1需要登陆,然后进行操作one")

def test_two():
    print("用例2不需要登陆,直接操作two")

def test_three(case):
    print("用例3需要登陆,然后操作three")

if __name__ == "__main__":
    pytest.main(["-s", "test_mfixture.py"])

Testing started at 10:33 ...
F:\\pytest_study\\venv\\Scripts\\python.exe "D:\\JetBrains\\PyCharm Community Edition 2020.2\\plugins\\python-ce\\helpers\\pycharm\\_jb_pytest_runner.py" --path F:/pytest_study/test_case/test_d/test_mfixture.py
Launching pytest with arguments F:/pytest_study/test_case/test_d/test_mfixture.py in F:\\pytest_study\\test_case\\test_d

============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- F:\\pytest_study\\venv\\Scripts\\python.exe
cachedir: .pytest_cache
metadata: Python: 3.7.0, Platform: Windows-10-10.0.19041-SP0, Packages: pytest: 6.2.4, py: 1.10.0, pluggy: 0.13.1, Plugins: reportlog: 0.1.2, allure-pytest: 2.8.12, cov: 2.8.1, forked: 1.1.3, html: 2.0.1, metadata: 1.8.0, ordering: 0.6, xdist: 1.31.0, JAVA_HOME: D:\\\\jdk-11.0.8
rootdir: F:\\pytest_study\\test_case\\test_d
plugins: reportlog-0.1.2, allure-pytest-2.8.12, cov-2.8.1, forked-1.1.3, html-2.0.1, metadata-1.8.0, ordering-0.6, xdist-1.31.0
collecting ... collected 3 items

test_mfixture.py::test_one              这个是登陆功能!
PASSED                                  [ 33%]用例1需要登陆,然后进行操作one

test_mfixture.py::test_two PASSED       [ 66%]用例2不需要登陆,直接操作two

test_mfixture.py::test_three            这个是登陆功能!
PASSED                                  [100%]用例3需要登陆,然后操作three


============================== 3 passed in 0.02s ==============================

进程已结束,退出代码 0

4.1.2 方式二:使用mark.usefixtures

# -*- coding:utf-8 -*-
# 作者:NoamaNelson
# 日期:2022/11/17 
# 文件名称:test_mfixture.py
# 作用:fixture的使用
# 联系:VX(NoamaNelson)
# 博客:https://blog.csdn.net/NoamaNelson


import pytest

# 不带参数时默认scope="function"
@pytest.fixture
def case():
    print("这个是登陆功能!")

def test_one(case):
    print("用例1需要登陆,然后进行操作one")

def test_two():
    print("用例2不需要登陆,直接操作two")

@pytest.fixture
def case1():
    print("输入验证码")

def test_three(case):
    print("用例3需要登陆,然后操作three")

@pytest.mark.usefixtures("case", "case1")
def test_four(case1):
    print("先登录,再输入验证码,最后操作four")

if __name__ == "__main__":
    pytest.main(["-s", "test_mfixture.py"])

test_mfixture.py::test_one                    这个是登陆功能!
PASSED                                        [ 25%]用例1需要登陆,然后进行操作one

test_mfixture.py::test_two PASSED             [ 50%]用例2不需要登陆,直接操作two

test_mfixture.py::test_three                  这个是登陆功能!
PASSED                                        [ 75%]用例3需要登陆,然后操作three

test_mfixture.py::test_four                   这个是登陆功能!
                                              输入验证码
PASSED                                       [100%]先登录,再输入验证码,最后操作four


============================== 4 passed in 0.03s ==============================

进程已结束,退出代码 0

4.1.3 方式三:使用autouse=True

# -*- coding:utf-8 -*-
# 作者:NoamaNelson
# 日期:2022/11/17 
# 文件名称:test_mfixture.py
# 作用:fixture的使用
# 联系:VX(NoamaNelson)
# 博客:https://blog.csdn.net/NoamaNelson


import pytest

# 不带参数时默认scope="function"
@pytest.fixture
def case():
    print("这个是登陆功能!")

def test_one(case):
    print("用例1需要登陆,然后进行操作one")

def test_two():
    print("用例2不需要登陆,直接操作two")

@pytest.fixture
def case1():
    print("输入验证码")

def test_three(case):
    print("用例3需要登陆,然后操作three")

@pytest.mark.usefixtures("case", "case1")
def test_four(case1):
    print("先登录,再输入验证码,最后操作four")

@pytest.fixture(autouse=True)
def case2():
    print("所有用例都会调用case2")

if __name__ == "__main__":
    pytest.main(["-s", "test_mfixture.py"])

test_mfixture.py::test_one                    所有用例都会调用case2
                                              这个是登陆功能!
PASSED                                        [ 25%]用例1需要登陆,然后进行操作one

test_mfixture.py::test_two                    所有用例都会调用case2
PASSED                                        [ 50%]用例2不需要登陆,直接操作two

test_mfixture.py::test_three                  所有用例都会调用case2
                                              这个是登陆功能!
PASSED                                        [ 75%]用例3需要登陆,然后操作three

test_mfixture.py::test_four                   所有用例都会调用case2
                                              这个是登陆功能!
                                              输入验证码
PASSED                                       [100%]先登录,再输入验证码,最后操作four


============================== 4 passed in 0.03s ==============================

进程已结束,退出代码 0

4.2 总结

  • 类前加 @pytest.mark.usefixtures() ,代表类里面所有测试用例都会调用该fixture
  • 可叠加多个 @pytest.mark.usefixtures() ,先执行的放底层,后执行的放上层
  • 可以传多个fixture参数,先执行的放前面,后执行的放后面
  • 如果fixture有返回值,用 @pytest.mark.usefixtures() 是无法获取到返回值的,必须用传参的方式
  • 不是test开头,加了装饰器也不会执行fixture。

以上是关于pytest学习和使用6-fixture如何使用?的主要内容,如果未能解决你的问题,请参考以下文章

pytest文档6-fixture之yield实现teardown

pytest学习和使用18-pytest.ini配置文件如何使用?

pytest学习和使用5-Pytest和Unittest中的断言如何使用?

pytest学习和使用17-Pytest如何重复执行用例?(pytest-repeat)

pytest学习和使用15-Pytest用例失败如何重跑?(pytest-rerunfailures的简单使用)

pytest学习和使用15-Pytest用例失败如何重跑?(pytest-rerunfailures的简单使用)