pytest解读fixtures之Teardown处理yield和addfinalizer方案

目录
  • 前言
  • 一、yield fixtures(推荐)
    • 1, yield 和 return
    • 2, yield的执行顺序
  • 二、addfinalizer
    • 1.request.addfinalizer把函数变成终结器
    • 2.request.addfinalizer注册多个终结器函数
    • 3.yield和addfinalizer的区别

前言

当我们运行测试函数时,我们希望确保测试函数在运行结束后,可以自己清理掉对环境的影响。这样的话,它们就不会干扰任何其他的测试函数,更不会日积月累的留下越来越多的测试数据。

用过unittest的朋友相信都知道teardown这个函数,做的是一样的事情,那么下面姑且就把这种“善后”工作的代码叫做teardown代码吧。

而pytest中的fixture,也提供了这样一个非常有用的系统,我们可以在里面定义teardown代码。

这里可以使用2种方式来实现,分别是yieldaddfinalizer

一、yield fixtures(推荐)

1, yield 和 return

在有yield的fixtures函数中,关键字yield 可以代替 return,可以把fixture里的一些对象传递给调用它们的fixture函数或者测试函数。

就像其他普通的fixture函数一样。区别仅仅是:

  • yield替换掉了return
  • teardown代码放置在yield之后

2, yield的执行顺序

pytest在执行fixture函数时,会根据fixture函数之间的线性关系顺序调用的。但是,当测试函数运行结束的时候,pytest又会按照之前的顺序反方向来执行fixture中yield之后的代码。结合示例看下,这里没有引用官方示例了,手写一个直观些的:

import pytest
@pytest.fixture
def fixture_one():
    print("\n执行fixture_one")
    return 1
@pytest.fixture
def fixture_two(fixture_one):
    print("\n执行fixture_two")
    yield 2
    print("\n执行fixture_two的teardown代码")
@pytest.fixture
def fixture_adding(fixture_one, fixture_two):
    print("\n执行fixture_adding")
    result = fixture_one + fixture_two
    yield result
    print("\n执行fixture_adding的teardown代码")
def test_demo(fixture_two, fixture_adding):
    print("\n执行测试函数test_demo")
    assert fixture_adding == 3

代码中,fixture中调用多个fixture,测试函数中调用多个fixture,通过前面几章的接触,相信大家这时候已经可以梳理出前后调用顺序了:

  • test_demo 测试函数,先去调用fixture函数 fixture_two,然后调用 fixture_adding。
  • 在fixture函数 fixture_two中,又会去调用另一个fixture函数 fixture_one。
  • 在fixture函数 fixture_adding中,调用了 fixture_one、fixture_two。

所以,fixture函数的先后顺序是:fixture_onefixture_twofixture_adding。那么,可以得知测试结束后的teardown代码执行顺序:fixture_addingfixture_two

运行一下代码,验证下结果是否符合我们的梳理:

============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: D:\练习\demo_fixture
plugins: allure-pytest-2.8.32, celery-4.3.0, Faker-4.14.2, base-url-1.4.2, html-2.1.1, metadata-1.10.0collected 1 item
test_module.py
执行fixture_one
执行fixture_two
执行fixture_adding
.
执行测试函数test_demo
执行fixture_adding的teardown代码
执行fixture_two的teardown代码
                                                         [100%]
============================== 1 passed in 0.09s ==============================

结果与我们刚才梳理的一致。

但是,值得注意的是,就算是teardown的代码是按照正确的顺序执行,也不能保证代码能正常执行的。比如说teardown里的某些代码执行异常了,导致别的清理动作也没法执行。这里就涉及到另一个点了:健壮的fixture结构应该是什么样子。这个官方文档另起进行说明,这里同样。

二、addfinalizer

1.request.addfinalizer把函数变成终结器

在pytest中想要做teardown的处理,除了使用带有yield的fixture函数,还可以直接添加终结器。直接来看示例代码:

import pytest
@pytest.fixture()
def demo_fixture(request):
    print("\n这个fixture在每个case前执行一次")
    def demo_finalizer():
        print("\n在每个case完成后执行的teardown")
    #注册demo_finalizer为终结函数
    request.addfinalizer(demo_finalizer)
def test_01(demo_fixture):
    print("\n===执行了case: test_01===")
def test_02(demo_fixture):
    print("\n===执行了case: test_02===")

看下运行结果:

============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: D:\练习\demo_fixture
plugins: allure-pytest-2.8.32, celery-4.3.0, Faker-4.14.2, base-url-1.4.2, html-2.1.1, metadata-1.10.0collected 2 items
test_module.py
这个fixture在每个case前执行一次
.
===执行了case: test_01===
在每个case完成后执行的teardown
这个fixture在每个case前执行一次
.
===执行了case: test_02===
在每个case完成后执行的teardown
                                                        [100%]
============================== 2 passed in 0.10s ==============================
Process finished with exit code 0

运行结果可以看出,效果与yield是一致的。这算是一个固定写法,关于request文档中也有另外的讲解,届时再分享。

2.request.addfinalizer注册多个终结器函数

上方代码是一个终结函数,如果要注册多个呢?

import pytest
@pytest.fixture()
def demo_fixture(request):
    print("\n这个fixture在每个case前执行一次")
    def demo_finalizer():
        print("\n在每个case完成后执行的teardown")
    def demo_finalizer2():
        print("\n在每个case完成后执行的teardown2")
    #注册demo_finalizer为终结函数
    request.addfinalizer(demo_finalizer)
    request.addfinalizer(demo_finalizer2)
def test_01(demo_fixture):
    print("\n===执行了case: test_01===")
def test_02(demo_fixture):
    print("\n===执行了case: test_02===")
if __name__ == '__main__':
    pytest.main(['-s', 'test_module.py'])

运行结果:

============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: D:\练习\demo_fixture
plugins: allure-pytest-2.8.32, celery-4.3.0, Faker-4.14.2, base-url-1.4.2, html-2.1.1, metadata-1.10.0collected 2 items
test_module.py
这个fixture在每个case前执行一次
.
===执行了case: test_01===
在每个case完成后执行的teardown2
在每个case完成后执行的teardown
这个fixture在每个case前执行一次
.
===执行了case: test_02===
在每个case完成后执行的teardown2
在每个case完成后执行的teardown
                                                        [100%]
============================== 2 passed in 0.09s ==============================
Process finished with exit code 0

这里要注意的是,多个终结器的情况下,执行的顺序是与注册时候相反的。

3.yield和addfinalizer的区别

目前从官方文档中看到的是

We have to be careful though, because pytest will run that finalizer once it’s been added, even if that fixture raises an exception after adding the finalizer.

一旦添加了终结器,pytest便会执行。

但是,当我尝试在setup代码中进行抛错,终结器的代码却并没有执行。

尝试搜索外网暂时也没得到有效的帮助,只能在GitHub上向pytest提了issue了,这里算是埋下一个坑,待后续解决,更多关于Teardown处理yield addfinalizer的资料请关注我们其它相关文章!

(0)

相关推荐

  • pytest官方文档解读fixtures的autouse

    现在我们已经知道了,fixtures是一个非常强大的功能. 那么有的时候,我们可能会写一个fixture,而这个fixture所有的测试函数都会用到它. 那这个时候,就可以用autouse自动让所有的测试函数都请求它,不需要在每个测试函数里显示的请求一遍. 具体用法就是,将autouse=True传递给fixture的装饰器即可. import pytest @pytest.fixture def first_entry(): return "a" @pytest.fixture de

  • pytest解读fixtures之Teardown处理yield和addfinalizer方案

    目录 前言 一.yield fixtures(推荐) 1, yield 和 return 2, yield的执行顺序 二.addfinalizer 1.request.addfinalizer把函数变成终结器 2.request.addfinalizer注册多个终结器函数 3.yield和addfinalizer的区别 前言 当我们运行测试函数时,我们希望确保测试函数在运行结束后,可以自己清理掉对环境的影响.这样的话,它们就不会干扰任何其他的测试函数,更不会日积月累的留下越来越多的测试数据. 用

  • pytest官方文档解读fixtures调用fixtures及fixture复用性

    目录 fixtures调用其他fixtures及fixture复用性 一.Fixtures调用别的Fixtures 二.Fixtures的复用性 fixtures调用其他fixtures及fixture复用性 pytest最大的优点之一就是它非常灵活. 它可以将复杂的测试需求简化为更简单和有组织的函数,然后这些函数可以根据自身的需求去依赖别的函数. fixtures可以调用别的fixtures正是灵活性的体现之一. 一.Fixtures调用别的Fixtures 直接看一个简单示例: import

  • pytest官方文档解读fixture的调用方式

    目录 pytest官方文档fixtures调用 一.测试函数声明传参请求fixture 二.fixture中的返回值传递给测试函数 pytest官方文档fixtures调用 既然fixtures是给执行测试做准备工作的,那么pytest如何知道哪些测试函数 或者 fixtures要用到哪一个fixtures呢?说白了,就是fixtures的调用. 一.测试函数声明传参请求fixture 测试函数通过将fixture声明为参数来请求fixture. def test_my_fruit_in_bas

  • pytest官方文档解读fixtures

    目录 一.测试的构成 二.回到fixtures本身 在深入了解fixture之前,让我们先看看什么是测试. 一.测试的构成 其实说白了,测试就是在特定的环境.特定的场景下.执行特定的行为,然后确认结果与期望的是否一致. 就拿最常见的登录来说,完成一次正常的登录场景,需要可用的测试环境,可以正常登录的账号和密码.然后,用这个账号密码进行登录操作,结果登录成功,符合这次正常登录场景的预期结果,测试完成. 这样看来,通常就可以把测试分为4个步骤: Arrange Act Assert Cleanup

  • pytest解读一次请求多个fixtures及多次请求

    目录 一.一个测试函数/fixture一次请求多个fixture 二.每个测试函数可以多次请求fixtures(返回值被缓存) 跟着节奏继续来探索fixtures的灵活性. 一.一个测试函数/fixture一次请求多个fixture 在测试函数和fixture函数中,每一次并不局限于请求一个fixture.他们想要多少就可以要多少. 下面是另一个简单的例子: import pytest # Arrange @pytest.fixture def first_entry(): return "a&

  • pytest解读fixtures中yield与addfinalizer区别

    目录 前言 一.问题回顾 二.问题解决 前言 在上一章中,文末留下了一个坑待填补,疑问是这样的: 目前从官方文档中看到的是 We have to be careful though, because pytest will run that finalizer once it’s been added, even if that fixture raises an exception after adding the finalizer. 一旦添加了终结器,pytest便会执行. 但是,当我尝试

  • pytest解读fixture有效性及跨文件共享fixtures

    目录 fixture有效性及跨文件共享fixtures 一.fixture有效性 二.跨文件共享fixtures fixture有效性及跨文件共享fixtures 一.fixture有效性 fixture有效性,说白了就是fixture函数只有在它定义的使用范围内,才可以被请求到.比如,在类里面定义了一个fixture,那么就只能是这个类中的测试函数才可以请求.但是,如果一个fixture定义的范围是整个模块,那么这个模块下的每个测试函数都可以去请求. 这里还有另一个影响fixture有效性的参

  • Pytest实现setup和teardown的详细使用详解

    前言 用过unittest的童鞋都知道,有两个前置方法,两个后置方法:分别是 setup() setupClass() teardown() teardownClass() Pytest也贴心的提供了类似setup.teardown的方法,并且还超过四个,一共有十种 模块级别:setup_module.teardown_module 函数级别:setup_function.teardown_function,不在类中的方法 类级别:setup_class.teardown_class 方法级别:

  • 简单了解pytest测试框架setup和tearDown

    pytest的setup与teardown 1)pytest提供了两套互相独立的setup 与 teardown和一对相对自由的setup与teardown 2)模块级与函数级 模块级(setup_module/teardown_module) #开始于模块始末(不在类中) 函数级(setup_function/teardown_function) #只对函数用例生效(不在类中) 3)方法级与类级 方法级(setup_method/teardown_method) #开始于方法始末(在类中) 类

随机推荐