Fixture作用域之scope
用于控制Fixture的作用范围,作用类似于Pytest的setup/teardown
scope参数可以是session, module,class,function,默认为function,作用范围顺序:session》module》class》function
名称 | 范围 | 说明 |
function | 函数级 | 每一个函数或方法都会调用 |
class | 类级别 | 每个测试类只运行一次 |
module | 模块级 | 每一个.py文件调用一次,scope="module" 可以实现多个.py 跨文件共享前置 |
session | 会话级 | 每次会话只需要运行一次,会话内所有方法及类,模块都共享这个方法,scope="session" 以实现多个.py 跨文件使用一个 session 来完成多个用例 |
在使用之前我们先来了解什么是conftest.py?
实现测试用例的过程中,当你发现需要使用来自多个文件的fixture函数的时候,可以将这些fixture函数放到conftest.py中。
你不需要导入这些fixture函数,它会由pytest自动检索。
fixture函数的检索顺序是从测试类开始,然后测试的模块,然后就是conftest.py文件,最后是内置的插件和第三方插件。
scope="session"
session的作用范围是针对.py级别的,module是对当前.py生效,seesion是对多个.py文件生效
session只作用于一个.py文件时,作用相当于module
所以session多数与contest.py文件一起使用,做为全局Fixture
#conftest.py,这个名称是固定的不能修改 import pytest @pytest.fixture(scope="session", autouse="True") def login(): print('所有接口都要用到登录token') username = "zhangsan" password = "123456" #相当于return,但是return不会执行后面的语句,yield可以 yield username,password print("完成登录操作")
#用例文件test.py import pytest def test_one(login): print("接口需要登录token") username,passwd =login print(f"用户名:{username},用户密码:{passwd}") if __name__ == '__main__': pytest.main()
============================= test session starts =============================
collecting ... collected 1 item
test1.py::test_one
============================== 1 passed in 0.09s ==============================
所有接口都要用到登录token
PASSED [100%]接口需要登录token
用户名:zhangsan,用户密码:123456
完成登录操作
return 与 yield的区别:
- return:在程序函数中返回某个值,返回之后函数不在继续执行,彻底结束。
- yield: 带有yield的函数是一个迭代器,函数返回某个值时,会停留在某个位置,返回函数值后,会在前面停留的位置继续执行直到程序结束
scope = “class”:
当测试类内的每一个测试方法都调用了fixture,fixture只在该class下所有测试用例执行前执行一次
测试类下面只有一些测试方法使用了fixture函数名,这样的话,fixture只在该class下第一个使用fixture函数的测试用例位置开始算,后面所有的测试用例执行前只执行一次。而该位置之前的测试用例就不管。
@pytest.fixture(scope='class') def login(): print("输入账号密码登录") username = "zhangsan" passwd = "123456" return username,passwd class Test_Class(): def test_one(self): print("测试用例前置条件不需要登录") def test_two(self,login): print("测试用例前置条件需要登录,返回值为{}".format(login)) def test_three(self,login): print("测试用例前置条件需要登录,返回值为{}".format(login)) if __name__ == '__main__': pytest.main()
============================= test session starts =============================
collecting ... collected 3 items
test1.py::Test_Class::test_one
test1.py::Test_Class::test_two
test1.py::Test_Class::test_three
============================== 3 passed in 0.10s ==============================
PASSED [ 33%]测试用例前置条件不需要登录
输入账号密码登录
PASSED [ 66%]测试用例前置条件需要登录,返回值为('zhangsan', '123456')
PASSED [100%]测试用例前置条件需要登录,返回值为('zhangsan', '123456')
scope = “module”:
与class相同,只从.py文件开始引用fixture的位置生效
@pytest.fixture(scope='module') def login(): print("输入账号密码登录") username = "zhangsan" passwd = "123456" return username,passwd def test_four(login): print("测试用例4前置条件需要登录,返回值为{}".format(login)) class Test_Class(): def test_one(self): print("测试用例1前置条件需要登录,返回值为{}".format(login)) def test_two(self): print("测试用例2前置条件需要登录,返回值为{}".format(login)) def test_three(self): print("测试用例3前置条件需要登录,返回值为{}".format(login)) if __name__ == '__main__': pytest.main()
============================= test session starts =============================
collecting ... collected 4 items
test1.py::test_four
test1.py::Test_Class::test_one
test1.py::Test_Class::test_two
test1.py::Test_Class::test_three
============================== 4 passed in 0.09s ==============================
输入账号密码登录
PASSED [ 25%]测试用例4前置条件需要登录,返回值为('zhangsan', '123456')
PASSED [ 50%]测试用例1前置条件需要登录,返回值为<function login at 0x00000263300FCAE0>
PASSED [ 75%]测试用例2前置条件需要登录,返回值为<function login at 0x00000263300FCAE0>
PASSED [100%]测试用例3前置条件需要登录,返回值为<function login at 0x00000263300FCAE0>
autouse:
默认False,若为True,刚每个测试函数都会自动调用该fixture,无需传入fixture函数名
由此我们可以总结出调用fixture的三种方式:
1、函数或类里面方法直接传fixture的函数参数名称
2、使用装饰器@pytest.mark.usefixtures()修饰
3、autouse=True自动调用,无需传仍何参数,作用范围跟着scope走(谨慎使用)
@pytest.fixture(scope='function',autouse="True") def login(): print("输入账号密码登录") username = "zhangsan" passwd = "123456" return username,passwd def test_one1(): print("测试用例1前置条件需要登录") def test_two2(): print("测试用例1前置条件需要登录") def test_three3(): print("测试用例1前置条件需要登录") def test_four4(): print("测试用例1前置条件需要登录")
============================= test session starts =============================
collecting ... collected 4 items
test1.py::test_one1
test1.py::test_two2
test1.py::test_three3
test1.py::test_four4
============================== 4 passed in 0.10s ==============================
输入账号密码登录
PASSED [ 25%]测试用例1前置条件需要登录
输入账号密码登录
PASSED [ 50%]测试用例1前置条件需要登录
输入账号密码登录
PASSED [ 75%]测试用例1前置条件需要登录
输入账号密码登录
PASSED [100%]测试用例1前置条件需要登录v
fixture参数化之params
Fixture的可选形参列表,支持列表传入,默认None,每个param的值,fixture都会去调用执行一次,类似for循环,可与参数ids一起使用,作为每个参数的标识,详见ids
使用方法
在fixture方法上加装饰器@pytest.fixture(params=[1,2,3]),就会传入三个数据1,2,3,分别将这三个数据传入到用例当中。传入的数据需要使用一个固定的参数名request来接收,
被Fixture装饰的函数要调用是采用:Request.param(固定写法,如下图)
#conftest.py import pytest @pytest.fixture(scope="session", params=["zhangsan",123456]) def login(request): print(f'所有接口都要用到登录token,{request.param}') yield request.param print("完成登录操作")
@pytest.fixture(scope="session", params=[["zhangsan",123456],["lisi",123456]])
def login(request):
print(f'所有接口都要用到登录token,{request.param}')
yield request.param
print("完成登录操作")
#test.py
import pytest def test_get_user(login): print("接口需要登录token") print(f"用户信息{login}")
============================= test session starts =============================
collecting ... collected 2 items
test1.py::test_get_user[zhangsan]
test1.py::test_get_user[123456]
============================== 2 passed in 0.09s ==============================
所有接口都要用到登录token,zhangsan
PASSED [ 50%]接口需要登录token
用户信息zhangsan
完成登录操作
所有接口都要用到登录token,123456
PASSED [100%]接口需要登录token
用户信息123456
完成登录操作
============================= test session starts =============================
collecting ... collected 2 items
test1.py::test_get_user[login0]
test1.py::test_get_user[login1]
============================== 2 passed in 0.10s ==============================
所有接口都要用到登录token,['zhangsan', 123456]
PASSED [ 50%]接口需要登录token
用户信息['zhangsan', 123456]
完成登录操作
所有接口都要用到登录token,['lisi', 123456]
PASSED [100%]接口需要登录token
用户信息['lisi', 123456]
完成登录操作
ids:用例标识ID
与params配合使用,一对一关系
import pytest @pytest.fixture(scope="session", params=[["zhangsan",123456],["lisi",123456]],ids=["one","two"]) def login(request): print(f'所有接口都要用到登录token,{request.param}') yield request.param print("完成登录操作") def test_get_user(login): print("接口需要登录token") print(f"用户信息{login}")
fixturehe和@pytest.mark.parametrize结合的参数化
#conftest.py import pytes @pytest.fixture() def fixturefun(request): test = request.param print(test) return test
#test.py import pytest test_param = [(1,1),("2","2"),(False,False),(int,int)] @pytest.mark.parametrize("fixturefun",test_param,indirect=True) # indirect=True 声明fixturefun不是参数,而是一个函数 def test_001(fixturefun): assert fixturefun[0] == fixturefun[1]
if __name__ == '__main__':
pytest.main()
============================= test session starts =============================
collecting ... collected 4 items
test1.py::test_001[fixturefun0]
test1.py::test_001[fixturefun1]
test1.py::test_001[fixturefun2]
test1.py::test_001[fixturefun3]
============================== 4 passed in 0.09s ==============================
(1, 1)
PASSED [ 25%]('2', '2')
PASSED [ 50%](False, False)
PASSED [ 75%](<class 'int'>, <class 'int'>)
PASSED [100%]
fixture重命名之name
通常来说使用 fixture 的测试函数会将 fixture 的函数名作为参数传递,但是 pytest 也允许将fixture重命名,如果使用了name,那只能将name传如,函数名不再生效
在fixture方法上加装饰器@pytest.fixture(name=‘XXX’)
import pytest @pytest.fixture(name='fix') def fixturefun(): print("fixtur重新命名") @pytest.mark.usefixtures('fix') def test_one(): print("函数用例 1")
============================= test session starts =============================
collecting ... collected 1 item
test1.py::test_one
============================== 1 passed in 0.09s ==============================
fixtur重新命名
PASSED [100%]函数用例 1