fixture是pytest的一项核心特性,它提供了一种组织和管理测试依赖项(如初始化环境、创建资源、清理操作等)的有效机制。下面将对fixture进行深入讲解,包括其基本概念、作用、使用方式、特性以及高级应用:
目录
[fixture 生命周期](#fixture 生命周期)
一、基本概念
fixture 是指在执行特定测试之前设置特定环境或状态 ,以及在测试完成后清理或还原这些状态的一种机制。它可以用来准备任何测试所需的共享资源,如数据库连接、临时文件、网络服务、模拟数据等。fixture的核心价值在于:
- 标准化测试环境:确保每次运行测试时,都基于一致的初始条件,提高测试的可重复性和可靠性。
- 资源管理:自动处理资源的创建、使用和销毁,避免测试间资源泄漏或污染。
- 代码复用:通过模块化的设计,让多个测试能够共享相同的fixture,减少冗余代码。
二、fixture的定义与使用
fixture在pytest中通过装饰器 @pytest.fixture 标记。一个基本的fixture定义如下:
python
import pytest
@pytest.fixture
def example_fixture():
# 准备阶段:执行创建或初始化资源的逻辑
resource = create_some_resource()
yield resource # 这里yield语句使得测试函数可以在运行时访问到fixture返回的对象
# 清理阶段:在测试结束后执行清理逻辑
cleanup_resource(resource)
要使用fixture,只需将其作为参数传递给测试函数:
python
def test_example_usage(example_fixture):
# 测试函数在执行时,example_fixture对应的fixture会先被调用,
# 然后将返回值传递给测试函数作为参数
assert do_something_with(example_fixture) == expected_result
三、fixture特性
fixture 生命周期
fixture 的生命周期主要包括三个阶段:
setup: 当测试函数需要某个 fixture 时,pytest 会先调用对应的 fixture 函数。fixture 函数负责初始化所需资源、创建数据或设置特定环境状态。如果 fixture 依赖其他 fixture,那么这些依赖的 fixture 会按照依赖顺序先被 setup。
use: fixture 函数通过 yield 语句返回的值或对象,会在测试函数执行期间作为参数传入并使用。测试函数可以自由操作这些资源,执行测试逻辑。
teardown: 测试函数执行完毕后,pytest 会继续执行 yield 语句之后的清理代码,这里负责释放资源、删除临时文件、关闭数据库连接、恢复环境状态等操作,确保测试的副作用得到清除。
fixture参数:
@pytest.fixture(scope="作用域",autouse="是否自动执行",params="参数化",ids="参数别
名",name="固件别名")
我们分开来讲述这些参数的具体作用
scope(作用域)
控制fixture的作用域,可选值包括 "function"(默认为function)、"class"、"module"、"package" 或 "session"。作用域决定了fixture的生命周期和复用范围:
function: 每当有测试函数请求此fixture时,pytest会为其创建一个新的fixture实例。这意味着每个测试函数都会得到一个独立的fixture副本。
class : 在类中首个依赖此fixture的测试函数执行前,pytest会创建一个fixture实例。该实例会被类中所有依赖此fixture的测试函数共享。
module : 在模块中首个依赖此fixture的测试函数执行前,pytest会创建一个fixture实例。该实例会被模块内所有依赖此fixture的测试函数共享。
package : 在包中首个依赖此fixture的测试函数执行前,pytest会创建一个fixture实例。该实例会被包内所有依赖此fixture的测试函数共享。
session: 在测试会话开始时,pytest会创建一个fixture实例。该实例会被会话中所有依赖此fixture的测试函数共享。
python
@pytest.fixture(scope="module")
def module_level_fixture():
...
#scope='module' 只创建一次,会作用到整个模块
autouse(是否自动执行)
如果设为 True,则无需显式在测试函数签名中指定该fixture,它会自动应用于所有匹配其作用域的测试,默认为False。
python
@pytest.fixture(autouse=True)
def always_used_fixture():
print("测试前准备前置操作")
yield
print("测试完毕释放资源操作")
def test_example_usage():
# 测试函数在执行时,无需传入always_used_fixture对应的fixture也会被调用,
pass
@pytest.fixture(autouse=False)
def always_used_fixture():
print("测试前准备前置操作")
yield
print("测试完毕释放资源操作")
def test_example_usage(always_used_fixture):
# 测试函数在执行时,需传入always_used_fixture对应的fixture才被调用,
pass
params(参数化)
pytest 支持对 fixture 进行参数化,这意味着一个 fixture 可以根据不同的输入参数产生多种不同的预备状态。参数化 fixture 可以极大地提高测试的覆盖范围和灵活性。要实现参数化 fixture,可以使用 pytest.mark.parametrize
装饰器同时装饰 fixture 函数和依赖它的测试函数,或者直接在 fixture 函数上使用 params
关键字参数。以下是一个参数化 fixture 的示例:
python
import pytest
@pytest.fixture(params=[1, 2, 3])
def input_fixture(request):
value = request.param
return value
def test_multiply_by_two(input_fixture):
assert input_fixture * 2 == input_fixture + input_fixture
在这个例子中,input_fixture是一个参数化的fixture,其params关键字参数设置为列表[1, 2, 3]。这意味着每次test_multiply_by_two函数运行时,input_fixture将会接收到列表中的一个不同值。因此,test_multiply_by_two函数会被执行三次,分别使用参数值1, 2 和 3。
ids(参数别名)
为参数化fixture的每个实例提供易于识别的标识,便于在测试报告中区分。
python
@pytest.fixture(params=[1, 2, 3], ids=["one", "two", "three"])
def named_parametrized_fixture(request):
value = request.param
...
name( 固件别名**)**
自定义fixture的名称,仅在需要覆盖默认生成的fixture名称时使用。
python
@pytest.fixture(name="custom_name")
def my_fixture():
...
fixture依赖:
fixture之间可以相互依赖。一个fixture在其定义中可以通过接受其他fixture作为参数来声明依赖关系。当依赖的fixture被请求时,pytest会确保先执行依赖fixture的setup部分,再执行当前fixture,最后在测试结束时按照相反顺序执行清理操作。
python
@pytest.fixture
def database_connection():
conn = establish_db_conn()
yield conn
close_db_conn(conn)
@pytest.fixture
def initialized_table(database_connection):
create_table(database_connection)
populate_table(database_connection)
yield
truncate_table(database_connection)
def test_query(initialized_table):
rows = execute_query(initialized_table)
assert len(rows) > 0
在这里,initialized_table fixture依赖于database_connection fixture。当test_query 函数请求initialized_table 时,pytest会确保先执行database_connection 的setup部分(建立数据库连接),然后执行initialized_table 的setup(创建表并填充数据)。测试结束后,先清理initialized_table (清空表),再清理database_connection(关闭连接)。
四、conftest.py--集中管理fixture固件
conftest.py 是 pytest 测试框架中一个特殊且非常重要的文件,主要用于存放与测试相关的配置、全局设置以及可复用的fixture(固件)
创建conftest.py文件
在需要使用fixture或进行特定配置的目录下创建名为 conftest.py 的文件。可以创建在项目根目录下(影响整个项目),也可以创建在特定的测试子目录下(仅影响该目录及其子目录)。
在conftest.py文件中可以定义fixture固件,也可以通过 pytest.mark 装饰器为所有测试添加标记(markers)
使用conftest.py中的内容
无需在测试代码中显式导入或引用 conftest.py 。pytest会自动发现并加载所有有效的 conftest.py 文件,根据其位置确定其作用域,并在相应范围内应用其中定义的fixture和配置。
总结而言,conftest.py 文件是pytest框架中用于集中管理测试配置、定义可复用fixture以及定制测试执行流程的关键组件。通过在适当位置创建 conftest.py 并编写相应的代码,可以极大地提升测试代码的组织性、可维护性和执行效率。
五、什么情况下使用fixture?
需要共享资源或数据:当你有一组测试都需要访问同一个数据库、文件、网络连接或其他资源时,用fixture来统一管理和初始化这些资源,确保每个测试获得的是正确且独立的状态。
需要复杂的环境设置:如果你的测试需要复杂的环境配置,如启动服务、设置系统权限、模拟外部接口响应等,使用fixture来封装这些复杂的设置和清理逻辑,使测试代码聚焦于核心测试行为。
需要重复的初始化或清理:如果有多个测试需要进行相似的初始化(如填充固定测试数据)或清理(如删除临时文件、重置系统状态)操作,使用fixture可以避免在每个测试中重复编写这些代码。
需要参数化测试:当你希望一个测试用例针对不同输入参数运行多次时,可以使用参数化的fixture为测试函数提供一组或多组不同的预备环境或数据,从而增加测试覆盖率。
希望以上内容能帮助大家高效理解pytest框架的fixture固件,让fixture帮助测试变得更高效可靠