6-fixture如何使用?
- 1 引入
- 2 fixture参数说明
-
- 3 fixture的特点
- 4 fixture如何使用?
-
- 4.1 调用方式
-
- 4.1.1 方式一:直接传参
- 4.1.2 方式二:使用mark.usefixtures
- 4.1.3 方式三:使用autouse=True
- 4.2 总结
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]:
- 我们可看到有五个参数
scope
、params
、autouse
、ids
、name
,源码中也对着几个参数进行了说明,如下:
"""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_`` and then use
``@pytest.fixture(name='')``.
"""
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 方式一:直接传参
import pytest
@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
import pytest
@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
import pytest
@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。