前言
上一篇讲到setup和teardown可以实现在测试用例之前或之后加入一些操作,但这种操作是整个脚本全局有效,如果我想实现如下场景:用例1需要先登录;用例2不需要先登录;用例3需要先登录。很显然已经无法借助setup/teardown来实现了,这也就是本篇学习的目的:自定义测试用例的预置条件。
fixture优势
fixture,被称为测试夹具,相比于setup/teardown具有如下优势:
- 命名方式灵活,不再局限于setup和teardown这几个命名
- conftest.py配置里可以实现数据共享,不需要import就能自动找到这些配置
- scope="module"实现多个.py跨文件共享前置,每一个.py文件调用一次
- scope="session"实现多个.py跨文件使用一个session来完成所有用例前置
fixture源码:
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
"""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='')``.
"""
简单翻译:
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
"""
使用装饰器标记夹具的功能。
可以使用此装饰器(带或不带参数)来定义夹具功能。
后面可以引用夹具名称以使其功能在运行测试之前被调用:测试模块或类可以使用pytest.mark.usefixtures(fixturename)标记。
测试用例可以直接使用夹具名称作为输入参数,在这种情况下,将从夹具函数返回的夹具实例注入。
夹具可以使用``return''或``yield''语句将其值提供给测试用例。当使用yield语句时,yield语句之后的代码块将作为teardown去执行,而不管测试结果如何,必须执行一次。
:param scope: 有四个级别参数 "function" (默认), "class", "module", "package" or "session".
:param params: 可选参数列表,将导致对夹具功能和使用该功能的所有测试的多次调用。 当前参数在``request.param``中可用。
:param autouse: 如果为True,则为所有测试激活fixture,如果为False(默认值)则需要显式引用来激活fixture
:param ids: 字符串ID列表,每个字符串ID对应于params,因此它们是测试ID的一部分。如果没有提供ID,则从params自动生成它们。
:param name: 夹具的名称。默认为修饰函数的名称。
pytest.fixture参数传入
pytest.fixture(scope="function")
简单场景:用例1需要先登录;用例2不需要先登录;用例3需要先登录
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/10/7
# @Author : 记夕忆沧澜
# @File : test_fixt.py
# @Software: PyCharm
# @Desc:fixture使用
import pytest
# 不带参数时默认scope="function"
@pytest.fixture()
def login():
print("输入账号,密码先登录")
def test_s1(login):
print('\n用例1:登录成功,操作1111')
def test_s2():
print('\n用例2:不需要登录,操作2222')
def test_s3(login):
print('\n用例3:登录成功,操作3333')
if __name__ == '__main__':
pytest.main(['-s', 'test_fixt.py'])
运行结果
============================= test session starts =============================
platform win32 -- Python 3.8.6, pytest-6.1.1, py-1.9.0, pluggy-0.13.1
cachedir: .pytest_cache
rootdir: G:\pytest
collecting ... collected 3 items
test_fixt.py::test_s1 输入账号,密码先登录
PASSED [ 33%]
用例1:登录成功,操作1111
test_fixt.py::test_s2 PASSED [ 66%]
用例2:不需要登录,操作2222
test_fixt.py::test_s3 输入账号,密码先登录
PASSED [100%]
用例3:登录成功,操作3333
============================== 3 passed in 0.08s ==============================
备注:@pytest.fixture()
里面没有参数,那么默认scope="function",针对函数有效
conftest.py
上面的简单案例是在同一个.py文件中,多个用例调用同一个login夹具,如果有多个.py文件都需要调用该测试夹具的话,就不能把login夹具和用例写到同一个.py文件内。
很明显可以感觉到,此时需要有一个配置文件,单独去管理一些类似login这样的预置操作,pytest默认读取该配置文件里的配置,而这个就是今天要说的主角:conftest.py。
conftest.py配置需要注意以下点:
-
conftest.py
配置文件名称是固定的,不能修改 -
conftest.py
与运行的用例最好在同一个package下,并配有__init__.py
文件 - 不需要
import
导入conftest.py
,pytest会自动查找
参考脚本代码设计如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/10/8
# @Author : 记夕忆沧澜
# @File : conftest.py
# @Software: PyCharm
import pytest
@pytest.fixture()
def login():
print("输入账号,密码先登录")
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/10/8
# @Author : 记夕忆沧澜
# @File : test_fixt1.py
# @Software: PyCharm
import pytest
def test_s1(login):
print('\n用例1:登录成功,操作1111')
def test_s2():
print('\n用例2:不需要登录,操作2222')
def test_s3(login):
print('\n用例3:登录成功,操作3333')
if __name__ == '__main__':
pytest.main(['-s', 'test_fixt1.py'])
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/10/8
# @Author : 记夕忆沧澜
# @File : test_fixt2.py
# @Software: PyCharm
import pytest
def test_s4(login):
print('\n用例4:登录成功,操作4444')
def test_s5():
print('\n用例5:不需要登录,操作5555')
def test_s6(login):
print('\n用例6:登录成功,操作6666')
if __name__ == '__main__':
pytest.main(['-s', 'test_fixt2.py'])
单独运行test_fixt1.py
或test_fixt2.py
都可以调到login()
这个测试夹具,这样可以把一些公共操作单独拿出来放到conftest.py
文件。
下一篇:pytest6-fixture之yield实现teardown