Pytest Fixture夹具参数介绍
Fixture 是一些函数,pytest 会在执行测试函数之前(或之后)加载运行它们。可以用它做一些事情,比如数据库的链接操作之类
import pytest
@pytest.fixture()
def post_code():
return '010'
def test_postcode(post_code):
assert post_code == '010'
执行结果

预处理和后处理
很多时候需要在测试前进行预处理(如新建数据库连接),并在测试完成进行清理(关闭数据库连接)。
当有大量重复的这类操作,最佳实践是使用固件来自动化所有预处理和后处理。
Pytest 使用 yield 关键词将固件分为两部分,yield 之前的代码属于预处理,会在测试前执行;yield 之后的代码属于后处理,将在测试完成后执行。
以下测试模拟数据库查询,使用 Fixture来模拟数据库的连接关闭:
import pytest
@pytest.fixture()
def db():
print('Connection success')
yield
print('closed')
def search_user(user_id):
d = {
'0001': 'lowen'
}
return d[user_id]
def test_search(db):
assert search_user('0001') == 'lowen'
结果成功前后标识前后有数据库的连接和关闭操作

scope用法:作用域
固件的作用是为了抽离出重复的工作和方便复用,为了更精细化控制固件(比如只想对数据库访问测试脚本使用自动连接关闭的固件),pytest 使用作用域来进行指定固件的使用范围。
在定义固件时,通过 scope 参数声明作用域,可选项有:
1.function: 函数级,每个测试函数都会执行一次固件; 2.class: 类级别,每个测试类执行一次,所有方法都可以使用; 3.module: 模块级,每个模块执行一次,模块内函数和方法都可使用; 4.session: 会话级,一次测试只执行一次,所有被找到的函数和方法都可用; 5.package: 包级别,每个python包只执行一次;
默认的作用域为 function。
import pytest
@pytest.fixture(scope='function')
def func_scope():
print('方法级别')
@pytest.fixture(scope='module')
def mod_scope():
print('模块级别')
@pytest.fixture(scope='session')
def sess_scope():
print('会话级别')
@pytest.fixture(scope='class')
def class_scope():
print('类级别')
def test_multi_scope(sess_scope, mod_scope, func_scope):
pass
执行结果如下,可以清楚看到各固件的作用域和执行顺序:

对于类使用作用域,需要使用 pytest.mark.usefixtures (对函数和方法也适用)
import pytest
@pytest.fixture(scope='class')
def class_scope():
print('类级别前置')
yield
print('类级别后置')
@pytest.mark.usefixtures('class_scope')
class TestClassScope:
def test_1(self):
print("test_1方法")
def test_2(self):
print("test_2方法")

叠加usefixtures
如果一个方法或者一个class用例想要同时调用多个fixture,可以使用@pytest.mark.usefixture()进行叠加。注意叠加顺序,先执行的放底层,后执行的放上层。
import pytest
@pytest.fixture(scope='function')
def class_open():
print('方法级别前置')
yield
print('方法级别后置')
@pytest.fixture(scope='class')
def class_close():
print('类级别前置')
yield
print('类级别后置')
class TestClassScope:
@pytest.mark.usefixtures('class_open')
def test_1(self):
print("test_1方法")
@pytest.mark.usefixtures('class_open')
@pytest.mark.usefixtures('class_close')
def test_2(self):
print("test_2方法")
if __name__ == '__main__':
pytest.main(['-vs'])
autouse用法:自动执行
目前为止,所有固件的使用都是手动指定,或者作为参数,或者使用 usefixtures。
如果想让固件自动执行,可以在定义时指定 autouse 参数。
下面是两个自动计时固件,一个用于统计每个函数运行时间(function 作用域),一个用于计算测试总耗时(session 作用域)
注意下面的测试函数并都没有使用固件:
import pytest
@pytest.fixture(scope='session', autouse=True)
def timer_session_scope():
print("用例执行前")
yield
print("用例执行后")
def test_one():
print('test_one方法')
结果如下

我们可以看到,我们选择自动执行,即使我们没有选择使用,pytest也会给自动执行的。执行到对应的function级别。
name用法: name参数表示可以对fixture的名称进行重命名:
注意:通过name重命名后,继续使用以前的名字调用会报错。
import pytest
@pytest.fixture(name='anjing')
def login():
print('\n登录操作')
yield
print('\n退出登录!')
class TestLogin:
def test_01(self, anjing):
print('---用例01---')
def test_02(self):
print('---用例02---')
if __name__ == '__main__':
pytest.main(['-vs'])
import pytest
data = ['anjing', 'test', 'admin']
@pytest.fixture(params=data, ids=['user=anjing', 'user=test', 'user=admin'])
def login(request):
print('登录功能')
yield request.param
print('退出登录')
class TestCase:
def test_01(self, login):
print('---用例01---')
print(f'登录的用户名{login}')
def test_02(self):
print('---用例02---')
if __name__ == '__main__':
pytest.main(['-vs'])
params用法:参数化
假设现在有一批 API 需要测试对不同数据库的支持情况(对所有数据库进行相同操作),最简单的方法就是针对每个数据库编写一个测试用例,但这包含大量重复代码,如数据库的连接、关闭,查询等。
使用固件抽离出数据库的通用操作,每个 API 都能复用这些数据库固件,同时可维护性也得到提升。
import pytest
@pytest.fixture(params=[
('redis', '6379'),
('mysql', '6300')
])
def db_config(request):
print(f"数据库的配置:{request.param}")
return request.param
@pytest.fixture(autouse=True)
def db(db_config):
print(f'数据库连接了{db_config}')
yield
print(f'数据库关闭了{db_config}')
def test_api():
assert 1 == 1
执行结果:

pytest测试用例参数化 @pytest.fixture 与 @pytest.mark.parametrize 结合使用【用例参数化传入参数设置:indirect=True】
如果测试数据需要在 fixture 方法中使用,同时也需要在用例中使用,可以让 parametrize 的 indirect 参数为 True
当设置indirect = True时,pytest 会把 argnames 当做函数执行,将 argvalues 作为参数传入到 argnames 函数中
import pytest
test_user_data = ['Tom', 'Lowen']
# 方法名作为参数
@pytest.fixture(scope='module')
def login(request):
# 通过 request.param 获取参数
user = request.param
print(f"登录用户: {user}")
return user
@pytest.mark.parametrize("login", test_user_data, indirect=True)
def test_login(login):
a = login
print(f"用例中 login 的返回值; {a}")
assert a != None

夹具 yield和return的区别
夹具中可以使用return,yield关键字为测试函数提供值,推荐使用yield关键字,他们的区别如下:
- yield返回值后,后面的代码还会继续运行
- return返回值后,后面的代码不会继续运行

浙公网安备 33010602011771号