pytest框架进阶自学系列 | fixture的并列与嵌套调用

书籍来源:房荔枝 梁丽丽《pytest框架与自动化测试应用》

一边学习一边整理老师的课程内容及实验笔记,并与大家分享,侵权即删,谢谢支持!

附上汇总贴:pytest框架进阶自学系列 | 汇总_热爱编程的通信人的博客-CSDN博客


常用使用场景:相互依赖的fixture可能是多个,或提前准备的数据也可能是多个,或数据准备也可能有依赖和先后。

并列使用fixture

常用使用场景:前提条件有多个,可以并列使用多个fixture,多个@注解,在一个测试方法中使用时将传入的参数写多个就能解决此问题。

具体执行步骤:

(1)新建文件test_fixture_twofixtures-1.py。

(2)创建open_browser()函数。在函数上添加@pytest.fixture(scope="module")。

(3)创建login()函数。在函数上添加@pytest.fixture()。

(4)创建3个测试函数。在第一个测试函数test_soso的参数中传入open_browser、login的函数名,表示调用这两个函数。

代码如下:

import pytest

@pytest.fixture()
def login():
    print("这是个登录模块!")

@pytest.fixture(scope="module")
def open_browser():
    print('打开首页!')

def test_soso(open_browser, login):
    print('case1:登录后执行搜索')
    
def test_cakan():
    print('case2:不登录就看')
    
def test_cart(login):
    print('case3:登录,加购物车')

执行结果如下,由于open_browser函数上的fixture的层级范围是module,所以先执行open_browser,也就是最先执行的是测试方法test_soso,读到输入参数时先执行open_browser,然后执行login方法,再执行test_soso中的代码。之后正常执行test_cakan和test_cart方法,执行test_cart方法时先执行login方法。

D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_twofixtures.py 
Testing started at 10:33 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_twofixtures.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3

============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 3 items

test_fixture_twofixtures.py::test_soso 
test_fixture_twofixtures.py::test_cakan 
test_fixture_twofixtures.py::test_cart 

============================== 3 passed in 0.21s ==============================

Process finished with exit code 0
打开首页!
这是个登录模块!
PASSED                            [ 33%]case1:登录后执行搜索
PASSED                           [ 66%]case2:不登录就看
这是个登录模块!
PASSED                            [100%]case3:登录,加购物车

嵌套调用fixture

常用使用场景:有先后关系的嵌套调用,多个@注解依赖可以通过传参方式,测试方法调用时只需调用最后的那种方法fixture,然后调用其他的fixture。

在上面代码的基础上复制并修改,将login函数的参数中加入open_browser,所以下面的测试方法就不用添加open_browser了。

代码如下:

import pytest

@pytest.fixture()
def login(open_browser):
    print("这是个登录模块!")

@pytest.fixture(scope="module")
def open_browser():
    print('打开首页!')

def test_soso(login):
    print('case1:登录后执行搜索')

def test_cakan():
    print('case2:不登录就看')

def test_cart(login):
    print('case3:登录,加购物车')

执行结果如下:

D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_twofixtures-2.py 
Testing started at 10:43 ...
Launching pytest with arguments D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3\test_fixture_twofixtures-2.py in D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3

============================= test session starts =============================
platform win32 -- Python 3.7.7, pytest-5.4.1, py-1.11.0, pluggy-0.13.1 -- D:\SynologyDrive\CodeLearning\WIN\pytest-book\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\SynologyDrive\CodeLearning\WIN\pytest-book
collecting ... collected 3 items

test_fixture_twofixtures-2.py::test_soso 
test_fixture_twofixtures-2.py::test_cakan 
test_fixture_twofixtures-2.py::test_cart 

============================== 3 passed in 0.36s ==============================

Process finished with exit code 0
打开首页!
这是个登录模块!
PASSED                          [ 33%]case1:登录后执行搜索
PASSED                         [ 66%]case2:不登录就看
这是个登录模块!
PASSED                          [100%]case3:登录,加购物车

多个fixture的实例化顺序

由于存在fixture的并列关系、嵌套关系及不同作用域,所以需要仔细体会这些复杂关系及执行顺序,因此多个fixture的实例化顺序遵循以下原则:

  • 高级别作用域的实例化(例如:session)先于低级别作用域的(例如:class或者function)实例化;
  • 相同级别作用域的实例化,其实例化顺序遵循它们在测试用例中被声明的顺序,也就是形参的顺序,或者fixture之间的相互调用关系;
  • 自动应用autouse的fixture,先于其同级别的其他fixture实例化。

代码如下:

import pytest

order = []

@pytest.fixture(scope="session", autouse=True)
def session1():
    order.append("session1")

@pytest.fixture(scope="module", autouse=True)
def module1():
    order.append("module1")

@pytest.fixture(autouse=True)
def function1(function3):
    order.append("function1")

@pytest.fixture
def function3():
    order.append("function3")

@pytest.fixture(autouse=True)
def autouse1():
    order.append("autouse1")

@pytest.fixture(autouse=True)
def function2():
    order.append("function2")

def test_order(module1):
    assert order == ["session1", "module1", "autouse1", "function3", "function1", "function2"]
    assert order == ["session1"]

执行结果如下:

session1拥有最高级的作用域(session),即使在测试用例test_order中最后被声明,它也是第1个被实例化的参数(参照第1条原则)。

module1拥有仅次于session级别的作用域(module),所以它是第2个被实例化的参数(参照第1条原则)。

function1、function2、function3、autouse1同属于function级别的作用域。

从test_order(function1,module1,function2,session1)形参的声明顺序中可以看出function1比function2先被实例化(参照第2条原则)。

function1的定义中又显式地调用了function3,所以function3比function1先被实例化(参照第2条原则)。

autouse1的定义中使能了autouse标记,所以它会在同级别的fixture之前被实例化,也就是在function3、function1、function2之前被实例化(参照第3条原则)。

所以在这个例子中fixture被实例化的顺序为session1、module1、autouse1、function3、function1、function2。

注意:除了autouse的fixture,需要测试用例显示声明(形参),不声明的参数不会被实例化。多个相同作用域的autouse fixture,其实例化顺序遵循fixture函数名的排序。

fixture返回工厂函数

常用使用场景:如果需要在一个测试用例中,多次使用同一个fixture实例,相对于直接返回数据,更好的方法是返回一个产生数据的工厂函数,并且,对于工厂函数产生的数据,也可以在fixture中对其管理。这里介绍的是一种思路,代码如下:

import pytest

@pytest.fixture
def make_customer_record():
    created_records = []

    def _make_customer_record(name):
        record = models.Customer(name=name, orders=[])
        created_records.append(record)
        return record

    yield _make_customer_record

    for record in created_records:
        record.destroy()

def test_customer_records(make_customer_record):
    customer1 = make_customer_record("Lisa")
    customer2 = make_customer_record("Mike")
    customer3 = make_customer_record("Meredith")

高效地利用fixture实例

在测试期间,pytest只激活最少个数的fixture实例。如果拥有一个参数化的fixture,所有使用它的用例会在所创建的第一个fixture实例被销毁后,才会使用第二个实例。

下面这个例子使用了两个参数化的fixture,其中一个是模块级别的作用域,另一个是用例级别的作用域,并且使用print方法打印出它们的setup/teardown流程。

代码如下:

import pytest

@pytest.fixture(scope="module", params=["mod1", "mod2"])
def modarg(request):
    param = request.param
    print("  SETUP modarg", param)
    yield param
    print("  TEARDOWN modrag", param)

@pytest.fixture(scope="function", params=[1,2])
def otherarg(request):
    param = request.param
    print("  SETUP otherarg", param)
    yield param
    print("  TEARDOWN otherarg", param)

def test_0(otherarg):
    print("  RUN test0 with otherarg", otherarg)

def test_1(modarg):
    print("  RUN test1 with modarg", modarg)

def test_2(otherarg, modarg):
    print("  RUN test2 with otherarg {} and modarg {}".format(otherarg, modarg))

执行结果如下:

PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3> pytest -q -s test_minfixture.py
  SETUP otherarg 1
  RUN test0 with otherarg 1
.  TEARDOWN otherarg 1
  SETUP otherarg 2
  RUN test0 with otherarg 2
.  TEARDOWN otherarg 2
  SETUP modarg mod1
  RUN test1 with modarg mod1
.  SETUP otherarg 1
  RUN test2 with otherarg 1 and modarg mod1
.  TEARDOWN otherarg 1
  SETUP otherarg 2
  RUN test2 with otherarg 2 and modarg mod1
.  TEARDOWN otherarg 2
  TEARDOWN modrag mod1
  SETUP modarg mod2
  RUN test1 with modarg mod2
.  SETUP otherarg 1
  RUN test2 with otherarg 1 and modarg mod2
.  TEARDOWN otherarg 1
  SETUP otherarg 2
  RUN test2 with otherarg 2 and modarg mod2
.  TEARDOWN otherarg 2
  TEARDOWN modrag mod2

8 passed in 0.15s
PS D:\SynologyDrive\CodeLearning\WIN\pytest-book\src\chapter-3>

mod1的TEARDOWN操作完成后,才开始mod2的SETUP操作。用例test_0独立完成测试,用例test_1和test_2都使用了模块级别的modarg,同时test_2也使用了用例级别的otherarg。它们执行的顺序是,test_1先使用mod1,接着test_2使用mod1和otherarg 1/otherarg 2,然后test_1使用mod2,最后test_2使用mod2和otherarg 1/otherarg 2,也就是说test_1和test_2共用相同的modarg实例,最少化地保留fixture的实例个数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值