fixture
是 pytest 框架提供的一个特性,用于管理测试中的资源和设定。它可以在测试函数或测试类中使用,以提供测试所需的固定数据、对象或执行特定的操作。
fixture
提供了更灵活、可重用和可扩展的测试环境设置功能。fixture
可以在测试函数、测试类或整个测试模块级别使用,以满足不同的测试需求。fixture
可以在多个测试函数或测试类中共享,并可以在不同的测试模块中重复使用。fixture
可以接受参数,以便在测试时动态生成或配置测试数据和资源。fixture
,无需手动调用。import pytest
@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None)
def test():
pass
scope
:可以理解成 fixture
的作用域,默认:function,还有 class、module、package、session 四个。( session 是整个测试会话,即开始执行 pytest 到结束测试。)autouse
(默认):False
,需要用例手动调用该 fixture
;如果是 True
,所有作用域内的测试用例都会自动调用该 fixture
。name
(默认):装饰器的名称,同一模块的 fixture
相互调用建议写个不同的 name。import pytest
# 1.定义 fixture 。
@pytest.fixture
def login():
data = {}
print("\n登录完成!")
# 返回测试数据或对象给测试函数使用。
return data
def test_case_01():
print("无需登录的测试操作。")
# 2.通过 fixture 名称调用。
def test_case_02(login):
print("需要登录才能进行的测试操作。")
if __name__ == '__main__':
pytest.main(["-s"])
# [ 50%]无需登录的测试操作。
#
# 登录完成!
# [100%]需要登录才能进行的测试操作。
@pytest.mark.usefixtures(fixture_name)
import pytest
@pytest.fixture
def login():
data = {}
print("\n登录完成!")
return data
# 1.再添加一个 fixture。
@pytest.fixture
def authentication():
print("\n认证完成!")
def test_case_01():
print("无需登录的测试操作。")
# 2.使用装饰器指定。
@pytest.mark.usefixtures("login", "authentication")
def test_case_02():
print("需要登录并通过验证,才能进行的测试操作。")
if __name__ == '__main__':
pytest.main(["-s"])
# [ 50%]无需登录的测试操作。
#
# 登录完成!
# 认证完成!
# [100%]需要登录并通过验证,才能进行的测试操作。
import pytest
# 1.设置自动应用到每个测试函数中。
@pytest.fixture(autouse=True)
def query_database():
data = {}
print("\n完成数据库查询!")
return data
def test_case_01():
print("执行测试用例一。")
def test_case_02():
print("执行测试用例二。")
if __name__ == '__main__':
pytest.main(["-s"])
# 完成数据库查询!
# [ 50%]执行测试用例一。
#
# 完成数据库查询!
# [100%]执行测试用例二。
@pytest.mark.usefixtures()
,代表这个类里面所有测试用例都会调用该 fixture
:import pytest
@pytest.fixture
def query_database():
print("query_database")
# 在类上面声明。(所有用例都调用。)
@pytest.mark.usefixtures("query_database")
class Tests:
def test_case_01(self):
print("test_case_01")
def test_case_02(self):
print("test_case_02")
if __name__ == '__main__':
pytest.main(["-s"])
# query_database
# [ 50%]test_case_01
#
# query_database
# [100%]test_case_02
@pytest.mark.usefixtures()
,先执行的放底层,后执行的放上层:import pytest
@pytest.fixture
def query_database():
print("query_database")
@pytest.fixture
def authentication():
print("authentication")
# 叠加使用。(从下到上执行。)
@pytest.mark.usefixtures("query_database")
@pytest.mark.usefixtures("authentication")
def test_case_01():
print("test_case_01")
if __name__ == '__main__':
pytest.main(["-s"])
# authentication
# query_database
# [100%]test_case_01
@pytest.mark.usefixtures()
可以传多个 fixture
参数,先执行的放前面,后执行的放后面:import pytest
@pytest.fixture
def query_database():
print("query_database")
@pytest.fixture
def authentication():
print("authentication")
# 传多个参数。(从前到后执行。)
@pytest.mark.usefixtures("authentication", "query_database")
def test_case_01():
print("test_case_01")
if __name__ == '__main__':
pytest.main(["-s"])
# authentication
# query_database
# [100%]test_case_01
fixture
有返回值,用 @pytest.mark.usefixtures()
是无法获取到返回值的,必须用传参的方式:import json
import pytest
@pytest.fixture
def query_database():
# mock 返回的数据。
data = {
"code": 0,
"msg": "SUCCESS",
"user": {
"name": "jan",
"age": 18
}
}
print("query_database successes!")
return json.dumps(data)
# 1.装饰器无法获取返回值。
@pytest.mark.usefixtures("query_database")
def test_case_01():
print("test_case_01")
# 2.通过名称入参才能获取返回值。
def test_case_02(query_database):
print("test_case_02 get res=", json.loads(query_database))
if __name__ == '__main__':
pytest.main(["-s"])
# test_case_01
# test_case_02 get res= {'code': 0, 'msg': 'SUCCESS', 'user': {'name': 'jan', 'age': 18}}
如果
fixture
还想依赖其他fixture
,需要用函数传参的方式,不能用@pytest.mark.usefixtures()
的方式,否则会不生效。
import pytest
@pytest.fixture(scope="session")
def open_browser():
print("===打开浏览器===")
# 1.依赖其他 fixture 。
@pytest.fixture
# 通过 @pytest.mark.usefixtures 调用不生效。
# @pytest.mark.usefixtures("open_browser")
# 需要通过名称入参。
def query_database(open_browser):
print("===查询数据库===")
def test_case_01(query_database):
pass
if __name__ == '__main__':
pytest.main(["-s"])
# ===打开浏览器===
# ===查询数据库===
scope
范围实例化: session > package > module > class > functionfixture
遵循测试函数中声明的顺序,并遵循 fixture
之间的依赖关系。(在 fixture_A 里面依赖的 fixture_B ,则 fixture_B 优先实例化。)autouse=True
)的 fixture
将在显式使用(传参或装饰器)的 fixture
之前实例化。
fixture
需要搭配yield
关键字来开启 teardown 操作。
yield
关键字用于定义一个生成器函数。
生成器函数是一种特殊的函数,它不会像普通函数一样立即执行并返回一个结果,而是每次迭代时返回一个值,并在下一个迭代时从上次离开的位置继续执行。
通过使用 yield
关键字,生成器函数可以保存其状态,这使得它们在处理大量数据时非常高效。
代码示例:
import pytest
@pytest.fixture(scope="session")
def open_browser():
print("===打开浏览器===")
yield
print("===关闭浏览器===")
@pytest.fixture
def query_database(open_browser):
print("===开始查询数据库===")
name = "jan"
age = 18
yield name, age
print("===数据库查询完成===")
def test_case_01(query_database):
print("===执行测试用例1===")
# 获取返回结果。
print("返回:", query_database)
name, age = query_database
# 断言。
assert "jan" == name
assert 18 == age
def test_case_02(query_database):
print("===执行测试用例2===")
if __name__ == '__main__':
pytest.main(["-s"])
# ===打开浏览器===
#
# ===开始查询数据库===
# PASSED ===执行测试用例1===
# 返回: ('jan', 18)
# ===数据库查询完成===
#
# ===开始查询数据库===
# PASSED ===执行测试用例2===
# ===数据库查询完成===
#
# ===关闭浏览器===
yield
前面的代码,即 setup 部分已经抛出异常了,则不会执行 yield
后面的 teardown 内容。yield
后面的 teardown 内容还是会正常执行。@pytest.fixture(scope="module")
def smtp_connection():
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
yield smtp_connection # provide the fixture value
smtp_connection()
连接将测试完成执行后已经关闭,因为 smtp_connection()
对象自动关闭时,with
语句结束。import pytest
@pytest.fixture(scope="module")
def open_browser(request):
print("===打开浏览器===")
def close_browser():
print("===关闭浏览器===")
# addfinalizer 将 close_browser 函数注册为终结器。
request.addfinalizer(close_browser)
def test_case_01(open_browser):
print("===执行测试用例1===")
def test_case_02(open_browser):
print("===执行测试用例2===")
if __name__ == '__main__':
pytest.main(["-s"])
# ===打开浏览器===
# PASSED ===执行测试用例1===
# PASSED ===执行测试用例2===
# ===关闭浏览器===
request.addfinalizer()
前面的代码,即 setup 部分已经抛出异常了,则不会执行 request.addfinalizer()
的 teardown 内容。“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。