conftest介绍
pytest中定义个conftest.py来实现数据,参数,方法、函数的共享 。
conftest.py 的文件名称是固定的, pytest 会自动识别 该文件,可以理解成一个专门存放 fixture 的配置文件 。一个工程下可以建多个 conftest.py 文件 ,一般我们都是在工程根目录下设置的 conftest 文件,这样会起到一个全局的作用。 我们也可以在不同的子目录下放 conftest.py ,这样作用范围只能在该层级的子目录下生效。(控制作用域)
conftest.py三个功能
- 设置项目和fixture
- 加载插件:在
conftest.py
中初始化插件或外部库 - 指定钩子函数
- 设置全局变量、环境变量或模拟外部服务
层级化配置
目录结构示例:
project/
├── conftest.py # 作用范围:全局
└── tests/
├── conftest.py # 作用范围:tests 目录
└── subdir/
├── conftest.py # 作用范围:subdir 目录
└── test_b.py
优先级:子目录的 conftest.py 优先级高于父目录,同名 Fixture 会被覆盖。
conftest特点
- conftest.py可以跨.py文件调用 ,有多个.py文件调用时,可让conftest.py只调用了一次 fixture,或调用多次fixture
- conftest.py 与运行的用例 要在同一个pakage下 ,并且有init.py文件
- 不需要import导入 conftest.py,pytest用例会自动识别该文件,放到项目的根目录下就可以全局目录调用了,如果放到某个package下,那就在改package内有效,可有多个conftest.py
- conftest.py配置脚本名称是固定的,不能改名称
- conftest.py文件不能被其他文件导入
- 所有同目录测试文件运行前都会执行conftest.py文件
conftest使用场景
fixture适用于在同一个py文件中多个用例执行时的使用;而conftest.py方式适用于多个py文件之间的数据共享。
比如常见的有以下场景:
- 请求接口需要共享登录接口的token/session
- 多个case共享一套测试数据
- 多个case共享配置信息
conftest示例
项目结构

|---|----------------------------------------|
| | # conftest.py
|
| | |
| | import pytest
|
| | @pytest.fixture(scope="session")
|
| | # @pytest.fixture(scope="module")
|
| | # @pytest.fixture(scope="class")
|
| | # @pytest.fixture(scope="function")
|
| | def get_token():
|
| | token = 'qeehfjejwjwjej11sss@22'
|
| | print('conftest中輸出token:%s' % token)
|
| | return token
|
|---|-------------------------------------------------------------------|
| | # test_02.py
|
| | |
| | import pytest
|
| | |
| | class Test(object):
|
| | def test2(self, get_token):
|
| | token = 'qeehfjejwjwjej11sss@22'
|
| | print("【执行test02.py-Test类-test2用例,获取get_token:%s】" % get_token)
|
| | assert get_token == token
|
|---|-------------------------------------------------------------------|
| | # test_03.py
|
| | |
| | import pytest
|
| | |
| | class Test(object):
|
| | def test3(self, get_token):
|
| | token = 'qeehfjejwjwjej11sss@22'
|
| | print("【执行test03.py-Test类-test3用例,获取get_token:%s】" % get_token)
|
| | assert get_token == token
|
| | |
| | def test4(self, get_token):
|
| | token = 'qeehfjejwjwjej11sss@22'
|
| | print("【执行test03.py-Test类-test4用例,获取get_token:%s】" % get_token)
|
| | assert get_token == token
|
|---|------------------------------|
| | # run.py
|
| | |
| | import pytest
|
| | if __name__ == '__main__':
|
| | pytest.main(["-s"])
|
| | |
执行结果

- 当conftest.py中的fixture(scope="
session
")时,所有的测试py文件执行前执行一次 - 当conftest.py中的fixture(scope="
module
")时,每一个测试.py文件执行前都会执行一次conftest文件中的fixture - 当conftest.py中的fixture(scope="
class
")时,每一个测试文件中的测试类执行前都会执行一次conftest文件中的 - 当conftest.py中的fixture(scope="
function
")时,所有文件的测试用例执行前都会执行一次conftest文件中的fixture
conftest结合fixture使用
- scope参数为session:所有测试.py文件执行前执行一次
- scope参数为module:每一个测试.py文件执行前都会执行一次conftest文件中的fixture
- scope参数为class:每一个测试文件中的测试类执行前都会执行一次conftest文件中的
- scope参数为function:所有文件的测试用例执行前都会执行一次conftest文件中的fixture
yield实现teardown
每个测试用例完成后,应该做好资源回收,此时就需要使用到 teardown函数的善后工作了。
用 fixture 实现 teardown 并不是一个独立的函数,而是用 yield
关键字来开启 teardown 操作。
addfinalizer实现回收(用例后置处理)
除了 yield 可以实现 teardown ,在 request-context 对象中注册 addfinalizer 方法也可以实现终结函数。
在用法上, addfinalizer 跟 yield 是不同的,需要你去注册作为终结器使用的函数。
例如:增加一个函数,并且注册成终结函数。
@pytest.fixture(scope=" ")
范围依旧可控制作用域
|---|------------------------------------|
| | # conftest.py
|
| | |
| | import pytest
|
| | @pytest.fixture()
|
| | def get_token(request):
|
| | print("用例执行前")
|
| | token = 'qeehfjejwjwjej11sss@22'
|
| | |
| | def teardown01():
|
| | print("用例后置处理111")
|
| | def teardown02():
|
| | print("用例后置处理222")
|
| | request.addfinalizer(teardown01)
|
| | request.addfinalizer(teardown02)
|
| | |
| | return token
|
执行结果:

fixture参数传递
参数传递有两个方向,一个是case给conftest.py传递参数 ,另一个是case中pytest.mark.parametrize给用例传递参数,下面介绍一下常用的参数传递方式。
parametrize向下给case传递参数(参数化执行,不向fixture传递参数)
|---|-----------------------------------------------------------------------|
| | @pytest.fixture()
|
| | def ft_func(request):
|
| | data = "c"
|
| | return data
|
| | |
| | @pytest.mark.parametrize('list', ['a', 'b'])
|
| | def test_create_func(list, ft_func):
|
| | print(f"list参数化中数据:{list}\n fixture获取值:{ft_func}") # 执行两次,分别传入a和b参数
|
| | |
| | if __name__ == '__main__':
|
| | pytest.main(["-s"])
|
执行结果:

parametrize向上给fixture传递参数
当 indirect=True
时,prepareas才会当成一个函数 去执行,而不是一个参数,并将data(第二个参数)当做参数传入函数。
|---|---------------------------------------------------------------------|
| | import pytest
|
| | """传单个参数
|
| | 当 indirect=True,getuser才会当成一个函数去执行,而不是一个参数,并将data当做参数传入函数。
|
| | test_getuser(self, getuser),这里的getuser是获取fixture返回的值。
|
| | """
|
| | # @pytest.fixture(params=[1,2,3,'linda'])
|
| | @pytest.fixture()
|
| | def getuser(request):
|
| | user = request.param
|
| | print(f" 获取用户: {user}")
|
| | return user
|
| | |
| | |
| | data = ["lilei", "jojo", "hanmeimei"]
|
| | # 用英文哈,中文会被加密
|
| | ids = [f" mark input :{user} " for user in data]
|
| | |
| | |
| | @pytest.mark.parametrize("getuser", data, ids=ids, indirect=True)
|
| | class TestClass(object):
|
| | def test_getuser(self, getuser):
|
| | print(f"输出用户信息:{getuser}")
|
执行结果:

|---|--------------------------------------------------------------------|
| | import pytest
|
| | """传多个参数(结合字典的使用)"""
|
| | @pytest.fixture()
|
| | def getlogins(request):
|
| | param = request.param
|
| | print(f" 获取用户名: {param['username']} 获取密码:{param['password']}")
|
| | return param
|
| | |
| | |
| | data = [{"username": "jojo", "password": "123456"},
|
| | {"username": "hanmeimei", "password": "123456"},
|
| | {"username": "lilei", "password": "123456"}]
|
| | |
| | |
| | @pytest.mark.parametrize("getlogins", data, indirect=True)
|
| | def test_getlogin(getlogins):
|
| | print(f"用户名:{getlogins['username']} 密码:{getlogins['password']}")
|
执行结果:

|---|----------------------------------------------------------------------------|
| | import pytest
|
| | """一个装饰器加多个fixture+单参数传递"""
|
| | @pytest.fixture(scope="module")
|
| | def getusername(request):
|
| | username = request.param
|
| | print(f" username is {username}")
|
| | return username
|
| | |
| | |
| | @pytest.fixture(scope="module")
|
| | def getpassword(request):
|
| | password = request.param
|
| | print(f" password is {password}")
|
| | return password
|
| | |
| | |
| | data = [("jojo", "1"), ("lilei", "123654")]
|
| | |
| | |
| | @pytest.mark.parametrize("getusername,getpassword", data, indirect=True)
|
| | def test_getUserinfo(getusername, getpassword):
|
| | print(f"用户名:{getusername} 密码:{getpassword}")
|
执行结果:

|---|-------------------------------------------------------------------------------------------|
| | import pytest
|
| | """一个装饰器加多个fixture+多参数传递"""
|
| | @pytest.fixture(scope="module")
|
| | def get_user(request):
|
| | param = request.param
|
| | # print(type(param))
|
| | print(f"获取用户信息 用户:{param['name']} 年龄:{param['age']}")
|
| | return param
|
| | |
| | |
| | @pytest.fixture(scope="module")
|
| | def get_account(request):
|
| | param = request.param
|
| | # print(type(param))
|
| | print(f"获取账号信息: 账号:{param['username']}\n密码: {param['password']}")
|
| | return param
|
| | |
| | |
| | data = [({"name": "jojo", "age": "18"}, {"username": "jojo001", "password": "123456"}),
|
| | ({"name": "zhangSan", "age": "19"}, {"username": "zhangSan001", "password": "123451"})
|
| | ]
|
| | |
| | @pytest.mark.parametrize("get_user,get_account", data, indirect=True)
|
| | def test_get_user_info(get_user, get_account):
|
| | print(f"用户信息:{get_user} \n用户账号:{get_account}")
|
执行结果:

|---|-----------------------------------------------------------|
| | import pytest
|
| | """多个装饰器+多个fixture"""
|
| | @pytest.fixture()
|
| | def users(request):
|
| | user = request.param
|
| | print(f" 用户名:{user}")
|
| | return user
|
| | |
| | @pytest.fixture()
|
| | def pwds(request):
|
| | pwd = request.param
|
| | print(f" 密码:{pwd}")
|
| | return pwd
|
| | |
| | data1 = ["lilei", "jojo"]
|
| | data2 = ["1", "2"]
|
| | |
| | @pytest.mark.parametrize("users", data1, indirect=True)
|
| | @pytest.mark.parametrize("pwds", data2, indirect=True)
|
| | def test_getuserinfo(users, pwds):
|
| | print(f"用户名为:{users} 密码为:{pwds}")
|
执行结果:

|---|----------------------------------------------------------------|
| | import pytest
|
| | """fixture未传参数使用默认值/传参数使用参数"""
|
| | @pytest.fixture(scope="module")
|
| | def getusername(request):
|
| | username = "王五"
|
| | print(request)
|
| | if hasattr(request, "param"):
|
| | username = request.param
|
| | print(f" username is {username}")
|
| | return username
|
| | |
| | |
| | data = ["张三", "李四"]
|
| | |
| | # 使用fixture中默认参数
|
| | def test_user001(getusername):
|
| | print(f"用户名:{getusername}")
|
| | |
| | # 传参给fixture并使用
|
| | @pytest.mark.parametrize("getusername", data, indirect=True)
|
| | def test_user002(getusername):
|
| | print(f"用户名:{getusername}")
|
执行结果:
