前言
Fixture是pytest的非常核心功能之一戴尸,在不改變被裝飾函數(shù)的前提下對函數(shù)進(jìn)行功能增強(qiáng)粟焊,經(jīng)常用于測試用例前置和后置工作。與setup/teardown類似孙蒙,但更強(qiáng)大靈活项棠。
fixture的優(yōu)勢
- fixture命名方式靈活,不局限于 setup 和teardown 那幾個(gè)命名規(guī)則
- conftest.py 配置里可以實(shí)現(xiàn)數(shù)據(jù)共享挎峦,能夠自動(dòng)搜索需要的fixture
- fixture 配置不同的參數(shù)可以輕松實(shí)現(xiàn)跨文件香追、session會話共享
fixture工作原理
- 在普通函數(shù)上使用@Pytest.fixture()裝飾器,聲明函數(shù)為一個(gè)fixture函數(shù)
- 如果測試用例函數(shù)的參數(shù)列表中存在fixture的函數(shù)名或者使用@pytest.mark.usefixtures(fixture_name)
- 在測試執(zhí)行階段坦胶,pytest執(zhí)行用例之前執(zhí)行fixture函數(shù)功能
- 如果fixture裝飾的函數(shù)無返回值透典,相當(dāng)于用例前置操作,否則相當(dāng)于傳參
- 如果fixture設(shè)置了后置操作顿苇,則用例執(zhí)行完成后進(jìn)行執(zhí)行
fixture搜索順序
- 第一步:優(yōu)先搜索測試所在的模塊
- 第二步:搜索模塊同一文件路徑下的conftest.py
- 第三步:找不到再搜索上一層的conftest.py峭咒,直到項(xiàng)目根目錄
fixture 函數(shù)說明
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]:
"""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>')``.
"""
"""
翻譯:
可以使用此裝飾器(帶或不帶參數(shù))來定義fixture功能。
fixture功能的名稱可以在后面使用引用它會在運(yùn)行測試之前調(diào)用它:test模塊或類可以使用
pytest.mark.usefixtures(fixturename標(biāo)記)纪岁。
測試功能可以直接使用fixture名稱作為輸入?yún)?shù)凑队,在這種情況下,夾具實(shí)例從fixture返回功能將被注入幔翰。
fixture可以使用``return`` or``yield`` 語句返回測試函數(shù)所需要的參數(shù)值漩氨,在``yield``后面的代碼塊
不管測試的執(zhí)行結(jié)果怎么樣都會執(zhí)行
:arg scope: scope 有四個(gè)級別參數(shù) "function" (默認(rèn)), "class", "module" or "session".
:arg params: 一個(gè)可選的參數(shù)列表,它將導(dǎo)致多個(gè)參數(shù)調(diào)用fixture功能和所有測試使用它
:arg autouse: 如果為True遗增,自動(dòng)執(zhí)行fixture函數(shù)才菠,所有函數(shù)都可以使用。 如果為False(默認(rèn)值)
則手動(dòng)使用fixture才能調(diào)用
:arg ids: 每個(gè)字符串id的列表贡定,每個(gè)字符串對應(yīng)于params 這樣他們就是測試ID的一部分赋访。
如果沒有提供ID它們將從params自動(dòng)生成
:arg name: fixture的名稱。 默認(rèn)為裝飾函數(shù)的名稱缓待。 如果設(shè)置了name的值蚓耽,在使用fixture時(shí)需要
使用設(shè)置的name,否則無法識別
"""
fixture參數(shù)詳解
使用語法:
@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None)
def login():
print("我是login函數(shù)")
scope 參數(shù)
- 說明:可以認(rèn)為是fixture的作用域旋炒,默認(rèn):function步悠,還有class、module瘫镇、package鼎兽、session四個(gè)
- 區(qū)別:
取值 | 范圍 | 說明 |
---|---|---|
function | 函數(shù)級 | 每一個(gè)函數(shù)或方法都會調(diào)用 |
class | 類級 | 每個(gè)測試類只運(yùn)行一次 |
module | 模塊級 | 每一個(gè).py文件調(diào)用一次 |
session | 會話級 | 每次會話只需要運(yùn)行一次答姥,一般用于打開瀏覽器、啟動(dòng)APP谚咬、登錄等操作 |
關(guān)注點(diǎn)
- fixture可以給函數(shù)和類使用
- fixture通過測試用例參數(shù)方式使用鹦付,這樣fixture的返回值可以通過fixture名稱作為參數(shù)傳遞給測試用例,也可以使用@pytest.mark.usefixtures("login")方式择卦,但無法給測試用例傳遞fixture的返回值
- fixture函數(shù)內(nèi)可以調(diào)用其他fixture函數(shù)敲长,必須要通過函數(shù)參數(shù)方式
- 非測試用例的函數(shù)無法使用fixture
作用范圍示例:
scope = "function"
# _*_coding:utf-8 _*_
# @Time :2021/7/3 14:39
# @Author : king
# @File :test_fixture.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(scope="function")
def login():
print("我是login函數(shù)。秉继。")
return "king"
# fixture內(nèi)調(diào)用其他fixture
@pytest.fixture(scope="function")
def login_01(login):
print("我是調(diào)用了 login的fixture函數(shù)")
# 通過參數(shù)方式使用fixture
def test_01(login):
print("我是 test_01 測試用例\n")
# 通過@pytest.mark.usefixtures("login")調(diào)用fixture
@pytest.mark.usefixtures("login")
def test_02():
print("我是 test_02 測試用例\n")
# 通過參數(shù)方式使用調(diào)用其他fixture的fixture
def test_03(login_01):
print("我是 test_03 測試用例\n")
# 給類使用fixture
@pytest.mark.usefixtures("login")
class TestFixture:
def test_one(self):
print("我是類里面的 test_one 測試用例")
def test_two(self):
print("我是類里面的 test_two 測試用例")
# 非test開頭函數(shù)調(diào)用fixture
def reg_01(login):
print("我是reg函數(shù)\n")
# 非test開頭函數(shù)使用@pytest.mark.usefixtures("login")調(diào)用fixture
@pytest.mark.usefixtures("login")
def reg_02():
print("我是reg函數(shù)\n")
if __name__ == '__main__':
pytest.main(["-s", "test_fixture.py"])
執(zhí)行結(jié)果為:
在這里插入圖片描述
scope = "class"
示例:
# _*_coding:utf-8 _*_
# @Time :2021/7/3 15:35
# @Author : king
# @File :test_fixture_class.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(scope="class")
def login():
print("我是login函數(shù)祈噪。。")
return "king"
# 通過參數(shù)方式使用fixture
def test_01(login):
print("我是 test_01 測試用例\n")
# 通過參數(shù)方式使用fixture
def test_02(login):
print("我是 test_02 測試用例\n")
@pytest.mark.usefixtures("login")
class TestFixture:
def test_one(self):
print("我是類里面的 test_one 測試用例")
def test_two(self):
print("我是類里面的 test_two 測試用例")
if __name__ == '__main__':
pytest.main(["-s", "test_fixture_class.py"])
執(zhí)行結(jié)果:
在這里插入圖片描述
scope = "module"
示例:
# _*_coding:utf-8 _*_
# @Time :2021/7/3 16:52
# @Author : king
# @File :test_fixture_module.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(scope="module")
def login():
print("我是login函數(shù)尚辑。辑鲤。")
return "king"
# 通過參數(shù)方式使用fixture
def test_01(login):
print("我是 test_01 測試用例\n")
# 通過參數(shù)方式使用fixture
def test_02(login):
print("我是 test_02 測試用例\n")
@pytest.mark.usefixtures("login")
class TestFixture:
def test_one(self):
print("我是類里面的 test_one 測試用例")
def test_two(self):
print("我是類里面的 test_two 測試用例")
if __name__ == '__main__':
pytest.main(["-s", "test_fixture_module.py"])
執(zhí)行結(jié)果為:
在這里插入圖片描述
scope = "session"
- 注意:session代表會話級,就是從啟動(dòng)測試到結(jié)束測試杠茬,看作為一次session會話遂填,scope = "session"使用到后面conftest.py詳細(xì)講解
params 參數(shù)
- fixture的可選形參列表,支持列表傳入
- params 參數(shù)包含幾個(gè)澈蝙,調(diào)用時(shí)就會執(zhí)行幾次
- 可與參數(shù)ids一起使用吓坚,作為每個(gè)參數(shù)的標(biāo)識,詳見ids
- 需要使用時(shí)灯荧,參數(shù)調(diào)用寫法固定為:Request.param
示例:
# _*_coding:utf-8 _*_
# @Time :2021/7/3 17:13
# @Author : king
# @File :test_fixture_params.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(params=[1, 2, 3, 4, 5])
def login(request):
print("我是login函數(shù)礁击。。")
return request.param
# 通過參數(shù)方式使用fixture
def test_01(login):
print("我是 test_01 測試用例-params- {}\n".format(login))
if __name__ == '__main__':
pytest.main(["-s", "test_fixture_params.py"])
執(zhí)行結(jié)果:
在這里插入圖片描述
autouse 參數(shù)
- 默認(rèn)False
- 如果設(shè)置為True逗载,則每個(gè)測試函數(shù)都會自動(dòng)調(diào)用該fixture,無需傳入fixture函數(shù)名哆窿,作用范圍跟著scope走(注意使用)
示例:
# _*_coding:utf-8 _*_
# @Time :2021/7/3 17:25
# @Author : king
# @File :test_fixture_autouse.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(autouse=True)
def login():
print("我是login函數(shù)。厉斟。")
# 通過參數(shù)方式使用fixture
def test_01():
print("我是 test_01 測試用例\n")
if __name__ == '__main__':
pytest.main(["-s", "test_fixture_autouse.py"])
執(zhí)行結(jié)果:
在這里插入圖片描述
ids 參數(shù)
- 用例標(biāo)題挚躯,需要與params配合使用,一對一關(guān)系
未配置ids時(shí):
# _*_coding:utf-8 _*_
# @Time :2021/7/3 17:29
# @Author : king
# @File :test_fixture_ids.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(params=[1, 2, 3])
def login(request):
return request.param
# 通過參數(shù)方式使用fixture
def test_01(login):
print("我是 test_01 測試用例\n")
if __name__ == '__main__':
pytest.main(["-s", "test_fixture_ids.py"])
執(zhí)行結(jié)果:
[圖片上傳失敗...(image-7d1ae1-1625479261110)]
配置了ids的示例:
# _*_coding:utf-8 _*_
# @Time :2021/7/3 17:29
# @Author : king
# @File :test_fixture_ids.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(params=[1, 2, 3], ids=["case01", "case02", "case03"])
def login(request):
return request.param
# 通過參數(shù)方式使用fixture
def test_01(login):
print("我是 test_01 測試用例\n")
if __name__ == '__main__':
pytest.main(["-s", "test_fixture_ids.py"])
執(zhí)行結(jié)果:
在這里插入圖片描述
問題:
當(dāng)我們多個(gè)測試用例文件(test_*.py)的所有用例都需要用登錄擦秽、打開瀏覽器码荔、啟動(dòng)APP等功能來作為前置操作,那就不能把登錄感挥、打開瀏覽器缩搅、啟動(dòng)APP功能寫到某個(gè)用例文件,如何解決呢触幼?
解決方案:引入conftest.py文件硼瓣,為了解決上述問題,單獨(dú)管理一些全局的fixture
conftest.py使用
說明:
- pytest會默認(rèn)讀取conftest.py里面的所有fixture
- conftest.py 文件名稱是固定的置谦,不能隨意改動(dòng)
- conftest.py只對同一個(gè)package下的所有測試用例生效堂鲤,并且有init.py文件
- 不同目錄可以有自己的conftest.py亿傅,一個(gè)項(xiàng)目中可以有多個(gè)conftest.py
- pytest會自動(dòng)查找項(xiàng)目中的conftest.py文件,逐層往上查找
示例:
- 項(xiàng)目目錄
case
│ conftest.py
│ __init__.py
│
├─case01
│ conftest.py
│ test_01.py
│ __init__.py
│
└─case02
conftest.py
test_02.py
__init__.py
case/conftest.py
# _*_coding:utf-8 _*_
# @Time :2021/7/3 18:04
# @Author : king
# @File :conftest.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(scope="session")
def login():
print("我是case目錄的login")
@pytest.fixture(scope="session")
def open_browser():
print("我是case目錄的 open_browser")
case01/conftest.py
# _*_coding:utf-8 _*_
# @Time :2021/7/3 18:06
# @Author : king
# @File :conftest.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(scope="session")
def login():
print("我是case01目錄的login")
case01/conftest.py
# _*_coding:utf-8 _*_
# @Time :2021/7/3 18:04
# @Author : king
# @File :test_01.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
def test_01(login):
print("我是case01 里面test_01 測試用例")
def test_02(open_browser):
print("我是case01 里面test_02 測試用例")
case02/conftest.py
# _*_coding:utf-8 _*_
# @Time :2021/7/3 18:07
# @Author : king
# @File :conftest.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
import pytest
@pytest.fixture(scope="session")
def _login():
print("我是case02 目錄的login")
case02/test_02.py
# _*_coding:utf-8 _*_
# @Time :2021/7/3 18:04
# @Author : king
# @File :test_02.py
# @Software :PyCharm
# @blog :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【測試之路筆記】
def test_02(login):
print("我是case02的 test_02 測試用例")
在case目錄下瘟栖,執(zhí)行 pytest -s
在這里插入圖片描述
注意點(diǎn)
- 外層的fixture不能調(diào)用內(nèi)層的fixture
- 不同包下面的fixture只能當(dāng)前包使用葵擎,不能被其他包使用,例如case01下面的fixture不能被case02的測試函數(shù)使用
以上為內(nèi)容純屬個(gè)人理解慢宗,如有不足坪蚁,歡迎各位大神指正奔穿,轉(zhuǎn)載請注明出處!