fixture固定装置,也叫夹具,下文都称呼为夹具。
使用 @pytest.fixture 将一个函数标记成夹具。
夹具本质上是函数,它将在应用它的每个测试函数之前运行。
可以将夹具定义在conftest.py文件中,共享给多个测试使用。
pytest查找夹具的顺序:测试类>>测试模块>> conftest.py 文件>>内置插件和第三方插件。
附上官网说明:
pytest 夹具被设计为显式、模块化和可扩展的。
在基本层面上,测试函数通过将它们声明为参数来请求它们所需的夹具。
当 pytest 开始运行测试时,它会查看该测试函数签名中的参数,然后搜索与这些参数具有相同名称的夹具。一旦 pytest 找到它们,它就会运行这些固定装置,捕获它们返回的内容(如果有的话),并将这些对象作为参数传递给测试函数。
总结:
说了这么多,我理解的在pytest中使用夹具fixture的作用类似于unittest框架里的setup和teardown,用来管理测试用例的前置处理和后置处理。
2、夹具应用场景测试环境搭建和销毁
数据库连接和关闭
测试数据的共享
登录登出操作
浏览器打开关闭操作
二、怎么用夹具使用 @pytest.fixture 将一个函数标记成夹具。
1、将夹具名作为测试函数参数中传入新建 test_fixture.py 文件如下:
import pytest
@pytest.fixture()
def my_fixture():
print("this is my fixture")
return True
def test_one(my_fixture):
assert my_fixture is True
将夹具名 my_fixture 作为参数传入到了测试函数(测试用例)test_one中。运行命令:pytest -vs test_fixture.py
(venv) C:Users 57776PycharmProjectspytest-demotestfixture>pytest -vs test_fixture.py
=================================== test session starts ===================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:programspythonpython38python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\Program Files\Java\jdk1.8.0_271', 'foo': 'bar'}rootdir: C:Users 57776PycharmProjectspytest-demotestfixture
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 1 itemtest_fixture.py::test_one this is my fixture
PASSED=================================== 1 passed in 0.06s ===================================
(venv) C:Users 57776PycharmProjectspytest-demotestfixture>
说明:
pytest 运行测试函数 test_one 的时候,会先去运行它参数的同名夹具test_fixture,打印出了"this is my fixture",并且捕获到了夹具的返回值 True。
(1)fixture运行过程探究
使用 --setup-show (注意这里是双--)命令行参数来查看fixture的执行过程。
运行命令:pytest -v --setup-show test_fixture.py
(venv) C:Users 57776PycharmProjectspytest-demotestfixture>pytest -vs --setup-show test_fixture.py
==================================== test session starts ====================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:programspythonpython38python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\Program Files\Java\jdk1.8.0_271', 'foo': 'bar'}rootdir: C:Users 57776PycharmProjectspytest-demotestfixture
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 1 itemtest_fixture.py::test_one this is my fixture
SETUP F my_fixture
test_fixture.py::test_one (fixtures used: my_fixture)PASSED
TEARDOWN F my_fixture=================================== 1 passed in 0.05s ===================================
(venv) C:Users 57776PycharmProjectspytest-demotestfixture>
说明:
从执行结果看到,测试函数test_one运行前执行SETUP ,运行后执行TEARDOWN。很容易联想到unittest框架中测试用例执行前后的setup和teardown操作。
2、使用conftest.py文件如果我们有多个文件中的测试函数都使用相同的夹具,这个时候我们可以把夹具函数定义在conftest.py文件中。
新建 conftest.py文件如下:
import pytest
# 定义夹具函数
@pytest.fixture()
def my_fixture():
print("this is my fixture")
return True
修改 test_fixture.py 文件如下:
def test_one(my_fixture):
assert my_fixture is True
def test_two(my_fixture):
assert my_fixture is False
新建 test_fix.py 文件如下:
def test_three(my_fixture):
assert 'T' in str(my_fixture)
文件目录如下:
运行命令:pytest -v 根据测试用例收集规则,pytest会运行testfixture文件夹下的所有测试用例。
(venv) C:Users 57776PycharmProjectspytest-demotestfixture>pytest -v
==================================== test session starts ====================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:programspythonpython38python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\Program Files\Java\jdk1.8.0_271', 'foo': 'bar'}rootdir: C:Users 57776PycharmProjectspytest-demotestfixture
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 3 itemstest_fix.py::test_three PASSED [ 33%]
test_fixture.py::test_one PASSED [ 66%]
test_fixture.py::test_two FAILED [100%]====================================== FAILURES ======================================
_________________________________________ test_two _________________________________________my_fixture = True
def test_two(my_fixture):
> assert my_fixture is False
E assert True is Falsetest_fixture.py:17: AssertionError
----------------------------------------------------------- Captured stdout setup -----------------------------------------------------------
this is my fixture
================================= short test summary info =================================
FAILED test_fixture.py::test_two - assert True is False
================================ 1 failed, 2 passed in 0.51s ================================(venv) C:Users 57776PycharmProjectspytest-demotestfixture>
说明:
从执行结果可以看到,test_fixture.py 和test_fixt.py 文件中的测试函数,在pytest运行的时候都从conftest.py文件中找到了定义的夹具my_fixture。
pytest 查找夹具顺序:测试类>>测试模块>> conftest.py 文件>>内置插件和第三方插件。
3、使用装饰器@pytest.mark.userfixtures()语法糖:
pytest.mark.usefixtures(*args)
*args – The names of the fixture to use, as strings.
看到装饰器的参数就能晓得,usefixtures可以和一个或者多个fixture配合使用。
修改 test_fixture.py 文件如下:
import pytest
@pytest.fixture()
def my_fixture():
print("this is my fixture")
return True
@pytest.mark.usefixtures('my_fixture')
def test_one():
assert my_fixture is True
运行命令:pytest -vs test_fixture.py
(venv) C:Users 57776PycharmProjectspytest-demotestfixture>pytest -vs test_fixture.py
==================================== test session starts ====================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:programspythonpython38python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\Program Files\Java\jdk1.8.0_271', 'foo': 'bar'}rootdir: C:Users 57776PycharmProjectspytest-demotestfixture
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 1 itemtest_fixture.py::test_one this is my fixture
FAILED====================================== FAILURES ======================================
_________________________________________ test_one _________________________________________@pytest.mark.usefixtures('my_fixture')
def test_one():
> assert my_fixture is True
E assert my_fixture is Truetest_fixture.py:21: AssertionError
================================ short test summary info ================================
FAILED test_fixture.py::test_one - assert my_fixture is True
=================================== 1 failed in 0.49s ===================================(venv) C:Users 57776PycharmProjectspytest-demotestfixture>
说明:
从执行结果可以看到,使用这种方法并没有拿到夹具的返回值。
装饰器@pytest.mark.usefixtures是Pytest调用fixture的方法之一,与直接传入fixture不同的是,它无法获取到夹具的返回值。
这种方式的使用场景是被测试函数(测试用例)需要多个fixture的时候。
如果你想要模块中的每个测试用例都调用同一个夹具,你也可以使用pytestmark标记。
固定写法 :pytestmark = pytest.mark.usefixtures("my_fixture")
修改 test_fixture.py 文件如下:
import pytest
@pytest.fixture()
def my_fixture():
print("this is my fixture")
return True
pytestmark = pytest.mark.usefixtures("my_fixture")
def test_one():
assert my_fixture is True
def test_two():
assert my_fixture is False
运行命令:pytest -vs test_fixture.py
(venv) C:Users 57776PycharmProjectspytest-demotestfixture>pytest -vs test_fixture.py
================================== test session starts ==================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:programspythonpython38python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\Program Files\Java\jdk1.8.0_271', 'foo': 'bar'}rootdir: C:Users 57776PycharmProjectspytest-demotestfixture
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 2 itemstest_fixture.py::test_one this is my fixture
FAILED
test_fixture.py::test_two this is my fixture
FAILED==================================== FAILURES ====================================
_______________________________________ test_one _______________________________________def test_one():
> assert my_fixture is True
E assert my_fixture is Truetest_fixture.py:23: AssertionError
_______________________________________ test_two _______________________________________def test_two():
> assert my_fixture is False
E assert my_fixture is Falsetest_fixture.py:27: AssertionError
================================ short test summary info ================================
FAILED test_fixture.py::test_one - assert my_fixture is True
FAILED test_fixture.py::test_two - assert my_fixture is False
=================================== 2 failed in 0.52s ===================================(venv) C:Users 57776PycharmProjectspytest-demotestfixture>
说明:
测试环境test_one和test_two都使用了同一个夹具my_fixture。
4、使用夹具参数autouse=TRUEfixture夹具参数autouse ,默认值False ,则需要显式引用来激活夹具。值为TRUE的话则所有测试函数都可以使用该夹具。
修改 test_fixture.py 文件如下:
import pytest
@pytest.fixture(autouse=True)
def my_fixture():
print("this is my fixture")
return True
def test_one():
assert my_fixture is True
def test_two():
assert my_fixture is False
运行命令:pytest -vs test_fixture.py
三、夹具的进阶用法 1、夹具中使用夹具(venv) C:Users 57776PycharmProjectspytest-demotestfixture>pytest -vs test_fixture.py
================================== test session starts ==================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:programspythonpython38python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\Program Files\Java\jdk1.8.0_271', 'foo': 'bar'}rootdir: C:Users 57776PycharmProjectspytest-demotestfixture
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 2 itemstest_fixture.py::test_one this is my fixture
FAILED
test_fixture.py::test_two this is my fixture
FAILED==================================== FAILURES ====================================
_______________________________________ test_one _______________________________________def test_one():
> assert my_fixture is True
E assert my_fixture is Truetest_fixture.py:23: AssertionError
_______________________________________ test_two _______________________________________def test_two():
> assert my_fixture is False
E assert my_fixture is Falsetest_fixture.py:27: AssertionError
================================ short test summary info ================================
FAILED test_fixture.py::test_one - assert my_fixture is True
FAILED test_fixture.py::test_two - assert my_fixture is False
=================================== 2 failed in 0.52s ===================================(venv) C:Users 57776PycharmProjectspytest-demotestfixture>
新建 test_append.py如下:
import pytest
@pytest.fixture
def first_entry():
return "a"
@pytest.fixture
def order(first_entry):
return [first_entry]
def test_string(order):
order.append("b")
assert order == ["a", "b"]
说明:
在夹具函数order() 中使用夹具first_entry作为参数。
2、使用多个夹具修改test_append.py文件如下:
import pytest
@pytest.fixture
def first_entry():
return "a"
@pytest.fixture
def second_entry():
return 2
def test_order(first_entry, second_entry):
assert [first_entry, second_entry] == ["a", 2]
说明:
test_order()使用了2个夹具函数first_entry,second_entry作为参数。
reference:
Pytest - Fixtures
API Reference — pytest documentation
Fixtures reference — pytest documentation



