一 引言
在测试过程中,通常会包括三个步骤,前置、执行测试、后置,pytest框架中,提供了类似的函数。
模块级别:setup_module (前置) / teardown_module(后置) ->不在类中,全局的
函数级别:setup_function(前置)/ teardown_function(后置) ->不在类中
类级别: setup_class (前置) / teardown_class (后置) ->只在类中前后运行一次
方法级别:setup_method(前置)/ teardown_method (后置)->类中方法前后
用例级别:setup (前置)/ teardown (后置)->运行测试用例的前后
但是,这种前置、后置处理方式存在一定的缺陷。假如,一个测试类中,存在多个测试方法,每一个测试方法需要不同的前置、后置,该怎么处理会比较好呢?
pytest提供了一种高级的功能,fixture。可以通过实现文件共享机制,供模块、函数、类或者整个项目会话使用。
在conftest.py中实现文件共享机制,而且不需要import导入,pytest会自动去加载匹配对应的fixture。
二 fixture参数详解
@pytest.fixture(scope = "function", params=None, autouse=False, ids=None, name=None)
1、scope:控制fixture作用域,默认是function级别
session:每次会话只需要运行一次,会话内所有模块、类、方法,都共享这个fixture
module:每一个.py文件调用一次
class:每一个类中调用一次
function:每一个function或者类方法中都会调用
2、params:可选的参数列表
①Fixture可选的参数列表,支持列表传入
②默认为None,每个param的值。
③可通过request.param接受设置的返回值,params中有多少个元素,在测试时,引用次fixture的函数就会调用几次。
④可与参数ids一起使用,作为每个参数的标识。
3、autouse:是否自动执行设置的fixture,默认是False
当autouse为True时,自动执行定义的fixture。
当autouse为False时,测试函数需要调用定义的fixture,定义的fixture函数才能执行。
4、ids:指定每个字符串id
当有多个params时,针对每一个param,可以指定id,这个id将变成测试用例名字的一部分。如果没有提供id,id将自动生成。
5、name:fixture的重命名
默认是fixture函数的名称,可以通过name参数更改这个fixture的名称。更改后,如果这个fixture被调用,则使用更改后的名称。
三 fixture应用
3.1、函数名调用fixture
import pytest
@pytest.fixture
def fix_a():
print("调用fix_a")
def test_a(fix_a):
print("执行test_a用例")
执行结果
根据运行结果可以看出,先执行了前置fixture,再执行测试用例。
3.2、@pytest.mark.usefixtures调用fixture
import pytest
@pytest.fixture
def fix_a():
print("调用fix_a")
@pytest.mark.usefixtures('fix_a')
def test_a1(fix_a):
print("执行test_a1用例")
3.3、多参数调用fixture
@pytest.fixture(params=['a', 'b', 'c'], scope="function")
def fix_a2(request):
print(f'调用fixture:{request.param}')
def test_a2(fix_a2):
print('执行test_a2用例')
执行结果
从执行结果,可以看到生成了三条测试用例,所以fixture也支持参数化。
3.4、autouse使用
从上面例子来看,fixture都需要主动调用,才能生效。但有时需要让用例自动执行,这时候就需要添加autouse参数,让fixture自动执行。
@pytest.fixture(autouse=True)
def fix_a3(request):
case_name = request.node.name
print(f"开始执行{case_name}测试用例 ")
yield
print(f"{case_name}测试用例执行结束 ")
def test_a3():
print('test_a3')
assert "tom" == "tom"
执行结果
从执行结果看,设置了autouse=True后,定义的fixture自动执行。并且定义的fixture通过yield关键字隔离前置、后置,yield之前是前置,yield之后是后置。
3.5、fixture嵌套使用
@pytest.fixture()
def fix_a4():
print('调用fix_a4')
@pytest.fixture()
def fix_a5(fix_a4):
print('调用fix_a5')
def test_a4(fix_a5):
print('-----执行test_a4测试用例------')
执行结果
从执行结果看,调用fix_a5时,先调用fix_a4,再调用fix_a5。这个场景比较适用于多个测试场景组合,多个前置测试步骤时使用。
3.6、conftest.py共享机制
实际测试工作中,常需要在全局范围内使用同一个测试前置、后置操作。可以使用conftest.py,在conftest.py定义的fixture不需要进行import,pytest会自动查找使用。
3.7、有返回值的fixture
@pytest.fixture()
def fix_a6():
amount = round(32.567, 2)
yield amount
def test_a6(fix_a6):
# 通过一个变量接收fix_a6
amount = fix_a6
print(f'fix_a6接受到的数据时{amount}')
执行结果
3.8、@pytest.fixture与@pytest.mark.parametrize结合使用
@pytest.fixture()
def fix_a7(request):
print("fixture拿到的原始参数是:", request.param)
sum = request.param[0] + request.param[1]
yield sum
@pytest.mark.parametrize("fix_a7", [(1, 3), (2, 4)], indirect=True)
def test_a7(fix_a7):
print('-----执行test_a7测试用例------')
print(f"fixture返回的参数和是:{fix_a7}")
执行结果
首先,fixture做了参数化,需要在fixture中接受变量,传入request这个内置fixture。然后传入的变量,通过request的param接收,在@pytest.mark.parametrize中,将定义的xi_a7传入参数化中用例中。