赞
踩
上一章介绍了pytest的setup和teardown,但是不够完善且灵活性不够强。设想一个场景,我们在一个class中有5条用例,2条需要登录、3条不需要登录、 1条需要数据库、4条不需要数据库,这种场景使用setup/teardown实现不是很方便。 对于这种场景可以试用fixture(夹具)来满足。
fixture是pytest用于将测试前后进行预备、清理工作的代码处理机制。
相较于setup和teardown而言,有以下几点优势:
在case文件test_01_baseuse.py 中定义并使用
使用yield 关键字来实现fixture的前后置 yield前的为前置、 yield后的为后置
import pytest # 导入pytest @pytest.fixture() # 使用装饰器的方式定义一个fixture def my_fixture(): print() print("==========before test===========") print("init data") data = "abc" yield data # yield 之前是 case前之前, yield之后是 case后执行 print("=========after test=========") print("clean data") def test_01(my_fixture): # 将要使用的fixture 传入测试case中 c = my_fixture print("========================data========================") print("c:", c)
执行结果
将上述例子中的 yield后的代码去掉, yield换成return即可
import pytest @pytest.fixture() def my_fixture(): print() print("==========before test===========") print("init data") data = "abc" return data # 如果只需要前置 yield 改成return 即可 def test_01(my_fixture): # 将要使用的fixture 传入测试case中 c = my_fixture print("========================data========================") print("c:", c)
yield 的之前不写代码 在yield后写代码即可
import pytest
@pytest.fixture()
def my_fixture():
yield # yield 之前是 case前之前, yield之后是 case后执行
print("=========after test=========")
print("clean data")
def test_03_teardown(my_fixture): # 将要使用的fixture 传入测试case中
c = my_fixture
print("========================data========================")
print("c:", c)
上边学习了 fixture的基本使用,现在来看下 fixture的作用域(类似 setup\teardown 相关的可以作用在类上 测试case上 模块上)
@pytest.fixture(scope=“function”) 默认的
@pytest.fixture(scope=“class”) 类中只执行一次
@pytest.fixture(scope=“module”) 每一个.py文件调用一次
@pytest.fixture(scope=“session”) 总的执行一次
介绍这些内容之前先简单介绍下conftest.py
conftest.py文件是pytest框架中的一个特殊文件,用于定义共享的设置、夹具(fixture)和钩子函数(hook)。
当pytest运行时,它会自动搜索项目中的conftest.py文件,并根据其中的定义来加载夹具和钩子函数。conftest.py文件可以位于项目的根目录下,也可以位于子目录中,它们会在对应的作用域内生效。
conftest.py文件特点:
@pytest.fixture(scope=“function”) 是fixture默认的作用域 等效于 @pytest.fixture()
每一个函数或方法都会调用(前提是使用了这个fixture的),每个测试用例执行前都会执行一次function级别的fixture。
来看个例子
conftest.py (注意这里要以包的方式来写case即要有 init.py 文件) 中创建一个fixture
"""conftest"""
import pytest
import random
@pytest.fixture() # 或者写 @pytest.fixture(scope="function")
def func_scope_fixture():
r = random.random()
print("===========调用一次产生一次随机数==========")
print("r:=====", r)
return r
在 测试文件中使用这个fixture
"""test"""
def test_04_1(func_scope_fixture):
r = func_scope_fixture
print("======test_04_1======")
print(r)
def test_04_2(func_scope_fixture):
r2 = func_scope_fixture
print("======test_04_2======")
print(r2)
执行结果
可以看到 使用到这个fixture的地方都重新执行了 产生了新的随机数
每个测试类中只会调用一次(前提是测试类中使用了这个fixture)
看例子
conftest.py
@pytest.fixture(scope="class")
def cls_scope_fixture():
r = random.random()
print("===========同一个类中多次调用只会产生一次随机数==========")
print("r:=====", r)
return r
在 测试文件中使用这个fixture
class TestClsScope(object):
def test_01(self, cls_scope_fixture):
print("=======test_01=======")
print("r: ", cls_scope_fixture)
def test_02(self, cls_scope_fixture):
print("=======test_02=======")
print("r: ", cls_scope_fixture)
def test_03(self):
print("=======test_03=======")
执行结果
在这个类中只执行了一次 在这个类中再次使用的时候返回上次产生的值
module 即一个py文件,就是说如果你在一个测试文件中引用了多次同一个fixture 那么这个fixture只在这个py文件中执行一次,其他都是使用这一次的结果
conftest.py
@pytest.fixture(scope="module")
def module_scope_fixture():
r = random.random()
print("===========同一个文件中多次调用只会产生一次随机数==========")
print("r:=====", r)
return r
在 测试文件中使用这个fixture
def test_mod_01(module_scope_fixture): print("======test_mod_01======") print(module_scope_fixture) def test_mod_02(module_scope_fixture): print("======test_mod_02======") print(module_scope_fixture) class TestModuleScope(object): def test_01(self, module_scope_fixture): print("======test_cls_mod_01======") print(module_scope_fixture) def test_02(self, module_scope_fixture): print("======test_cls_mod_02======") print(module_scope_fixture)
执行结果
在这个测试文件中只执行了一次 文件中其他case再次使用的时候返回上次产生的值
当fixture的scope定义为session时,是指在当前目录下的所有用例之前和之后执行fixture对应的操作
fixture为session级别是可以跨.py模块调用的,也就是当我们有多个.py文件的用例的时候,如果多个用例只需调用一次fixture,那就可以设置为scope=“session”,且写到conftest.py文件里
来看例子
conftest.py
@pytest.fixture(scope="session")
def session_scope_fixture():
t = time.time()
print("current_time:---->", t)
return t
测试文件1
def test_session_1(session_scope_fixture):
print("=======test_session_1========")
print(session_scope_fixture)
测试文件2
def test_session_2(session_scope_fixture):
print("=======test_session_2========")
print(session_scope_fixture)
运行结果
可以看到在不同的测试文件中 只执行了一次
在编写fixture时可以通过传参的方式传入已有的fixture,来实现新的功能
来看例子
@pytest.fixture(scope="session")
def session_scope_fixture():
t = time.time()
print("current_time:---->", t)
return t
@pytest.fixture(scope="session")
def session_scope_date(session_scope_fixture):
dt = datetime.fromtimestamp(session_scope_fixture).date()
print("========current dt=======:", dt)
return dt
使用和上边使用一样
def test_session_1(session_scope_date):
print("=======test_session_1========")
print(session_scope_date)
结果
对于所有 case 都用到的 fixture 如果每个用例都传入,会很麻烦。fixture 有个 autouse 参数,默认是False没开启的,可以设置为True开启自动使用fixture功能,这样用例就不用每次都去传参了,autouse设置为True,自动调用fixture功能。
所有用例都会生效,包括类中的测试用例和类以外的测试用例。
conftest.py
@pytest.fixture(autouse=True)
def auto_use_scope():
print("===========每个 case 我都会执行==========")
t = time.time()
print("current_time:---->", t)
return t
"""autouse"""
def test_autouse_01():
print("test_autouse_01======")
def test_autouse_02():
print("test_autouse_02======")
def test_autouse_03():
print("test_autouse_03======")
def test_autouse_04():
print("test_autouse_04======")
执行结果
给 fixture 起名,然后使用起的这个新名字来
import pytest @pytest.fixture(name="new_fixture") def fixture_rename(): print("==========fixture_rename===") print("===========my name is new_fixture======") def test_rename(new_fixture): print("========test_rename========") @pytest.mark.usefixtures("new_fixture") def test_rename2(): print("========test_rename2========")
对于一个fixture场景我们有时候需要验证不同的输入的情况,那么可以使用 fixture 的params 传参来实现
import pytest
@pytest.fixture(params=[{"name": "张三", "age": 21}, {"name": "李四", "age": 22}, {"name": "王五", "age": 25}])
def fix_params(request):
return request.param
def test_param(fix_params):
# print("fix_params: =====>", fix_params)
print(f"我叫 {fix_params['name']}, 我今年{fix_params['age']}岁")
上述中虽然把所有的 param 都执行了,但是在测试结果中不能知道每次测试的输入是什么, 可以通过 ids 这个参数来给每次执行提供一个标记
import pytest
params = [{"name": "zs", "age": 21}, {"name": "ls", "age": 22}, {"name": "ww", "age": 25}]
@pytest.fixture(params=params, ids=[p['name'] for p in params])
def fix_params(request):
return request.param
def test_param(fix_params):
# print("fix_params: =====>", fix_params)
print(f"我叫 {fix_params['name']}, 我今年{fix_params['age']}岁")
官方内置的 fixture:https://docs.pytest.org/en/7.4.x/reference/fixtures.html#fixture
介绍几个常用的吧
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。