通过fixture可以把公共方法参数化,通过@pytest.fixture( )装饰的函数可以作为参数传入到测试用例中,当执行这个测试用例时会优先执行fixture装饰的函数,方便代码复用,减少冗余,可理解成公共方法的封装
场景:在执行把商品加入购物车的测试用例时,先要完成用户登录;接口自动化测试,执行购买时需要传入用户token;

  1. # test_mod8.py
  2. import pytest
  3. @pytest.fixture()
  4. def login():
  5. print("账号进行登录")
  6. def test_add_shopCar(login):
  7. print("把商品加入购物车")

执行结果:

  1. test_mod8.py::test_add_shopCar 账号进行登录
  2. 把商品加入购物车
  3. PASSED
  4. =================================================================================== 1 passed in 0.03s ====================================================================================

测试用例 test_add_shopCar中传入参数是login,因此执行test_add_shopCar测试用例时会首先执行login函数

  1. import pytest
  2. @pytest.fixture()
  3. def add_func():
  4. return 3+2
  5. def test_one(add_func):
  6. assert add_func > 4
  7. def test_two(add_func):
  8. assert add_func < 3
  1. test_mod8.py::test_one PASSED
  2. test_mod8.py::test_two FAILED
  3. ======================================================================================== FAILURES ========================================================================================

当testone和test_two测试用例接收 add_func 函数时, fixture的作用是实例化函数, 然后返回add func 的值;
在多个测试模块.py中共用一个fixture,可以把fixture移动到conftest.py中,pytest会自动导入,不需要手动导入
image.png

  1. # conftest.py
  2. import pytest
  3. @pytest.fixture()
  4. def login():
  5. print("账号进行登录")
  6. @pytest.fixture()
  7. def add_func():
  8. return 3+2
  1. # test_mod8.py
  2. import pytest
  3. def test_add_shopCar(login):
  4. print("把商品加入购物车")
  5. def test_one(add_func):
  6. assert add_func > 4
  7. def test_two(add_func):
  8. assert add_func < 3

执行结果:

  1. test_mod8.py::test_add_shopCar 账号进行登录
  2. 把商品加入购物车
  3. PASSED
  4. test_mod8.py::test_one PASSED
  5. test_mod8.py::test_two FAILED
  6. ======================================================================================== FAILURES ========================================================================================

上述代码,test_mod8.py 模块中test_add_shopCar,test_one, test_two测试函数中入参分别为conftest.py模块中fixture装饰的login(),add_func(),从而实现了fixture装饰下的函数前置功能,可供多个测试模块调用;
fixture 自动导入的顺序: 测试类 > 测试模块 > conftest.py

  1. # conftest.py
  2. import pytest
  3. @pytest.fixture()
  4. def re_str():
  5. return "这是conftest中的str"
  1. @pytest.fixture()
  2. def re_str():
  3. return "这是模块中的str"
  4. class TestMod8:
  5. @pytest.fixture()
  6. def re_str(self):
  7. return "这是class中的str"
  8. def test_three(self,re_str):
  9. print(re_str)

上述代码分别在测试类,测试模块,conftest.py中定义了re_str并通过@pytest.fixture()装饰
直接通过pytest运行test_three
结果:

  1. test_mod8.py::TestMod8::test_three 这是class中的str
  2. PASSED
  3. =================================================================================== 1 passed in 0.43s ====================================================================================

test_three中的re_str调用的是类中的re_str,如果注释掉类中的re_str再次运行

  1. @pytest.fixture()
  2. def re_str():
  3. return "这是模块中的str"
  4. class TestMod8:
  5. # @pytest.fixture()
  6. # def re_str(self):
  7. # return "这是class中的str"
  8. def test_three(self,re_str):
  9. print(re_str)

结果:

  1. test_mod8.py::TestMod8::test_three 这是模块中的str
  2. PASSED
  3. =================================================================================== 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修饰的函数名,也可完成调用 ```python

    conftest.py

    import pytest

@pytest.fixture(scope=”module”,autouse=True) def module_fixture(): print(“这是conftest.py中的module_fixture”)

  1. ```python
  2. # test_mod8
  3. def test_five():
  4. print("正在执行测试用例-------test_five")
  5. class TestMod8:
  6. def test_three(self):
  7. print("正在执行测试用例-------test_three")
  8. def test_four(self):
  9. print("正在执行测试用例-------test_four")

执行结果:

  1. test_mod8.py::test_five 这是conftest.py中的module_fixture
  2. 正在执行测试用例-------test_five
  3. PASSED
  4. test_mod8.py::TestMod8::test_three 正在执行测试用例-------test_three
  5. PASSED
  6. test_mod8.py::TestMod8::test_four 正在执行测试用例-------test_four
  7. PASSED
  8. =================================================================================== 3 passed in 0.56s ====================================================================================

任务:
针对上述代码,@pytest.fixture(scope=”module”,autouse=True) 把scope分别更改为function,class,package再运行

通过 fixture 参数化

  1. # test_mod8.py
  2. import pytest
  3. l = [1,2,3,4,5]
  4. @pytest.fixture(params=l)
  5. def read_params(request):
  6. s = request.param
  7. return s
  8. def test_five(read_params):
  9. x = read_params
  10. print(f"正在执行测试用例-------test_five,传入参数{x}")

命令行:pytest -vs test_mod8.py::test_five

  1. 结果:
  2. test_mod8.py::test_five[1] 正在执行测试用例-------test_five,传入参数1
  3. PASSED
  4. test_mod8.py::test_five[2] 正在执行测试用例-------test_five,传入参数2
  5. PASSED
  6. test_mod8.py::test_five[3] 正在执行测试用例-------test_five,传入参数3
  7. PASSED
  8. test_mod8.py::test_five[4] 正在执行测试用例-------test_five,传入参数4
  9. PASSED
  10. test_mod8.py::test_five[5] 正在执行测试用例-------test_five,传入参数5
  11. PASSED
  12. =================================================================================== 5 passed in 0.20s ====================================================================================

fixture 中传入的params参数为一个list,fixture装饰的函数传入request参数,通过request.param来遍历params中的元素并返回;
注:params是一个 list 类型

通过fixture + yield 实现setup和teardown

  1. # conftest.py
  2. import pytest
  3. @pytest.fixture(scope="function",autouse=True) #定义这个fixture作用域为每个测试用例都会调用
  4. def function_fixture():
  5. print("用例执行之前执行,相当于setup")
  6. yield # yield之前的代码在执行测试用例前执行
  7. # yield之后的代码在执行测试用例后执行
  8. print("用例执行之后执行,相当于teardown")
  1. import pytest
  2. class TestMod8:
  3. def test_three(self):
  4. print("正在执行测试用例-------test_three")
  5. def test_four(self):
  6. print("正在执行测试用例-------test_four")

执行结果:

  1. test_mod8.py::TestMod8::test_three 用例执行之前执行,相当于setup
  2. 正在执行测试用例-------test_three
  3. PASSED用例执行之后执行,相当于teardown
  4. test_mod8.py::TestMod8::test_four 用例执行之前执行,相当于setup
  5. 正在执行测试用例-------test_four
  6. PASSED用例执行之后执行,相当于teardown
  7. =================================================================================== 2 passed in 0.07s ====================================================================================