通过fixture可以把公共方法参数化,通过@pytest.fixture( )装饰的函数可以作为参数传入到测试用例中,当执行这个测试用例时会优先执行fixture装饰的函数,方便代码复用,减少冗余,可理解成公共方法的封装
场景:在执行把商品加入购物车的测试用例时,先要完成用户登录;接口自动化测试,执行购买时需要传入用户token;
# test_mod8.pyimport pytest@pytest.fixture()def login():print("账号进行登录")def test_add_shopCar(login):print("把商品加入购物车")
执行结果:
test_mod8.py::test_add_shopCar 账号进行登录把商品加入购物车PASSED=================================================================================== 1 passed in 0.03s ====================================================================================
测试用例 test_add_shopCar中传入参数是login,因此执行test_add_shopCar测试用例时会首先执行login函数
import pytest@pytest.fixture()def add_func():return 3+2def test_one(add_func):assert add_func > 4def test_two(add_func):assert add_func < 3
test_mod8.py::test_one PASSEDtest_mod8.py::test_two FAILED======================================================================================== FAILURES ========================================================================================
当testone和test_two测试用例接收 add_func 函数时, fixture的作用是实例化函数, 然后返回add func 的值;
在多个测试模块.py中共用一个fixture,可以把fixture移动到conftest.py中,pytest会自动导入,不需要手动导入
# conftest.pyimport pytest@pytest.fixture()def login():print("账号进行登录")@pytest.fixture()def add_func():return 3+2
# test_mod8.pyimport pytestdef test_add_shopCar(login):print("把商品加入购物车")def test_one(add_func):assert add_func > 4def test_two(add_func):assert add_func < 3
执行结果:
test_mod8.py::test_add_shopCar 账号进行登录把商品加入购物车PASSEDtest_mod8.py::test_one PASSEDtest_mod8.py::test_two FAILED======================================================================================== FAILURES ========================================================================================
上述代码,test_mod8.py 模块中test_add_shopCar,test_one, test_two测试函数中入参分别为conftest.py模块中fixture装饰的login(),add_func(),从而实现了fixture装饰下的函数前置功能,可供多个测试模块调用;
fixture 自动导入的顺序: 测试类 > 测试模块 > conftest.py
# conftest.pyimport pytest@pytest.fixture()def re_str():return "这是conftest中的str"
@pytest.fixture()def re_str():return "这是模块中的str"class TestMod8:@pytest.fixture()def re_str(self):return "这是class中的str"def test_three(self,re_str):print(re_str)
上述代码分别在测试类,测试模块,conftest.py中定义了re_str并通过@pytest.fixture()装饰
直接通过pytest运行test_three
结果:
test_mod8.py::TestMod8::test_three 这是class中的strPASSED=================================================================================== 1 passed in 0.43s ====================================================================================
test_three中的re_str调用的是类中的re_str,如果注释掉类中的re_str再次运行
@pytest.fixture()def re_str():return "这是模块中的str"class TestMod8:# @pytest.fixture()# def re_str(self):# return "这是class中的str"def test_three(self,re_str):print(re_str)
结果:
test_mod8.py::TestMod8::test_three 这是模块中的strPASSED=================================================================================== 1 passed in 0.20s ====================================================================================
当conftest.py中也存在re_str时,测试用例首先执行模块中的re_str,只有当类中的fixture与模块中的fixture都不存在时,才会引用conftest.py中的fixture
fixture中的scope参数
- fixture为session级别是可以跨.py模块调用的,也就是当我们有多个.py文件的用例的时候,如果多个用例只需调用一次fixture,那就可以设置为scope=”session”,并且写到conftest.py文件里。
- package 会作用于包内的每一个测试用例, 且只被调用一次
- function 每个测试用例都会调用一次
- class 如果一个class 中有多个测试用例, 在类中只调用一次
fixture中的autouse参数(True, False)
@pytest.fixture(scope=”module”,autouse=True) def module_fixture(): print(“这是conftest.py中的module_fixture”)
```python# test_mod8def test_five():print("正在执行测试用例-------test_five")class TestMod8:def test_three(self):print("正在执行测试用例-------test_three")def test_four(self):print("正在执行测试用例-------test_four")
执行结果:
test_mod8.py::test_five 这是conftest.py中的module_fixture正在执行测试用例-------test_fivePASSEDtest_mod8.py::TestMod8::test_three 正在执行测试用例-------test_threePASSEDtest_mod8.py::TestMod8::test_four 正在执行测试用例-------test_fourPASSED=================================================================================== 3 passed in 0.56s ====================================================================================
任务:
针对上述代码,@pytest.fixture(scope=”module”,autouse=True) 把scope分别更改为function,class,package再运行
通过 fixture 参数化
# test_mod8.pyimport pytestl = [1,2,3,4,5]@pytest.fixture(params=l)def read_params(request):s = request.paramreturn sdef test_five(read_params):x = read_paramsprint(f"正在执行测试用例-------test_five,传入参数{x}")
命令行:pytest -vs test_mod8.py::test_five
结果:test_mod8.py::test_five[1] 正在执行测试用例-------test_five,传入参数1PASSEDtest_mod8.py::test_five[2] 正在执行测试用例-------test_five,传入参数2PASSEDtest_mod8.py::test_five[3] 正在执行测试用例-------test_five,传入参数3PASSEDtest_mod8.py::test_five[4] 正在执行测试用例-------test_five,传入参数4PASSEDtest_mod8.py::test_five[5] 正在执行测试用例-------test_five,传入参数5PASSED=================================================================================== 5 passed in 0.20s ====================================================================================
fixture 中传入的params参数为一个list,fixture装饰的函数传入request参数,通过request.param来遍历params中的元素并返回;
注:params是一个 list 类型
通过fixture + yield 实现setup和teardown
# conftest.pyimport pytest@pytest.fixture(scope="function",autouse=True) #定义这个fixture作用域为每个测试用例都会调用def function_fixture():print("用例执行之前执行,相当于setup")yield # yield之前的代码在执行测试用例前执行# yield之后的代码在执行测试用例后执行print("用例执行之后执行,相当于teardown")
import pytestclass TestMod8:def test_three(self):print("正在执行测试用例-------test_three")def test_four(self):print("正在执行测试用例-------test_four")
执行结果:
test_mod8.py::TestMod8::test_three 用例执行之前执行,相当于setup正在执行测试用例-------test_threePASSED用例执行之后执行,相当于teardowntest_mod8.py::TestMod8::test_four 用例执行之前执行,相当于setup正在执行测试用例-------test_fourPASSED用例执行之后执行,相当于teardown=================================================================================== 2 passed in 0.07s ====================================================================================
