通过fixture可以把公共方法参数化,通过@pytest.fixture( )装饰的函数可以作为参数传入到测试用例中,当执行这个测试用例时会优先执行fixture装饰的函数,方便代码复用,减少冗余,可理解成公共方法的封装
场景:在执行把商品加入购物车的测试用例时,先要完成用户登录;接口自动化测试,执行购买时需要传入用户token;
# test_mod8.py
import 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+2
def test_one(add_func):
assert add_func > 4
def test_two(add_func):
assert add_func < 3
test_mod8.py::test_one PASSED
test_mod8.py::test_two FAILED
======================================================================================== FAILURES ========================================================================================
当test_one和test_two测试用例接收 add_func 函数时, fixture的作用是实例化函数, 然后返回add_ func 的值;
在多个测试模块.py中共用一个fixture,可以把fixture移动到conftest.py中,pytest会自动导入,不需要手动导入
# conftest.py
import pytest
@pytest.fixture()
def login():
print("账号进行登录")
@pytest.fixture()
def add_func():
return 3+2
# test_mod8.py
import pytest
def test_add_shopCar(login):
print("把商品加入购物车")
def test_one(add_func):
assert add_func > 4
def test_two(add_func):
assert add_func < 3
执行结果:
test_mod8.py::test_add_shopCar 账号进行登录
把商品加入购物车
PASSED
test_mod8.py::test_one PASSED
test_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.py
import 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中的str
PASSED
=================================================================================== 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 这是模块中的str
PASSED
=================================================================================== 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)
- 是否自动调用,即测试用例中不需要传入fixture修饰的函数名,也可完成调用
# conftest.py
import pytest
@pytest.fixture(scope="module",autouse=True)
def module_fixture():
print("这是conftest.py中的module_fixture")
# test_mod8
def 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_five
PASSED
test_mod8.py::TestMod8::test_three 正在执行测试用例-------test_three
PASSED
test_mod8.py::TestMod8::test_four 正在执行测试用例-------test_four
PASSED
=================================================================================== 3 passed in 0.56s ====================================================================================
任务:
针对上述代码,@pytest.fixture(scope="module",autouse=True) 把scope分别更改为function,class,package再运行
通过 fixture 参数化
# test_mod8.py
import pytest
l = [1,2,3,4,5]
@pytest.fixture(params=l)
def read_params(request):
s = request.param
return s
def test_five(read_params):
x = read_params
print(f"正在执行测试用例-------test_five,传入参数{x}")
命令行:pytest -vs test_mod8.py::test_five
结果:
test_mod8.py::test_five[1] 正在执行测试用例-------test_five,传入参数1
PASSED
test_mod8.py::test_five[2] 正在执行测试用例-------test_five,传入参数2
PASSED
test_mod8.py::test_five[3] 正在执行测试用例-------test_five,传入参数3
PASSED
test_mod8.py::test_five[4] 正在执行测试用例-------test_five,传入参数4
PASSED
test_mod8.py::test_five[5] 正在执行测试用例-------test_five,传入参数5
PASSED
=================================================================================== 5 passed in 0.20s ====================================================================================
fixture 中传入的params参数为一个list,fixture装饰的函数传入request参数,通过request.param来遍历params中的元素并返回;
注:params是一个 list 类型
通过fixture + yield 实现setup和teardown
# conftest.py
import pytest
@pytest.fixture(scope="function",autouse=True) #定义这个fixture作用域为每个测试用例都会调用
def function_fixture():
print("用例执行之前执行,相当于setup")
yield # yield之前的代码在执行测试用例前执行
# yield之后的代码在执行测试用例后执行
print("用例执行之后执行,相当于teardown")
import pytest
class TestMod8:
def test_three(self):
print("正在执行测试用例-------test_three")
def test_four(self):
print("正在执行测试用例-------test_four")
执行结果:
test_mod8.py::TestMod8::test_three 用例执行之前执行,相当于setup
正在执行测试用例-------test_three
PASSED用例执行之后执行,相当于teardown
test_mod8.py::TestMod8::test_four 用例执行之前执行,相当于setup
正在执行测试用例-------test_four
PASSED用例执行之后执行,相当于teardown
=================================================================================== 2 passed in 0.07s ====================================================================================