pytest之fixture之yield关键字实现teardown用例后置操作 || addfinalizer注册函数为终结函数

pytest之fixture之yield关键字实现teardown用例后置操作

前言

pytest之fixture函数使用(pytest测试框架测试固件 文中讲到,fixture函数是通过scope参数来控制setup级别;

②既然有setup函数作为用例之前的操作,测试用例执行完成之后那肯定也有teardown操作。

③但是fixture的teardown操作并不是独立的函数,用yield关键字唤醒teardown操作。【依然存在于fixture方法中】

yield实现teardown后置操作

实例1

#使用yield关键字实现teardown_xxx的功能
 
import pytest
 
# 此时,login函数是一个测试固件,相当于实现了setup_xxx&teardown_xxx的功能。
@pytest.fixture()
def login():
    ############# 以下的代码相当于setup部分 ###########
    print('登录系统')
    token = 'a1b23c'
    yield token
    ############# 以下的代码相当于teardown部分 ###########
    print('退出登录')
 
# 在测试函数里, 通过形参声明要使用的测试固件
def test1(login):
    # login参数的值是测试固件函数的返回值
    print('执行测试 test1: ', login)
    print('测试1')
 
def test2(login):
    print('执行测试 test2: ', login)
    print('测试2')
 
# 通过python解释器执行需要以下代码
if __name__ == '__main__':
    pytest.main(["-s", "test_yieldDemo.py"])

运行结果:

【注意】:return和yield两个关键字都可以返回值;

yield关键字返回值后,后面的代码还会继续运行;【由于实例1中fixture函数login需要返回token,而且还需要继续执行teardown后置操作:所以选择yield关键字所以后面代码还会继续运行】

return关键字返回值后,后面的代码不会继续运行;

实例2

import pytest
 
@pytest.fixture(scope="module")
def open():
    print("打开浏览器,并且打开百度首页")
    yield
    print("执行teardown!")
    print("最后关闭浏览器")
 
def test_s1(open):
    print("用例1:搜索python-1")
 
def test_s2(open):
    print("用例2:搜索python-2")
 
def test_s3(open):
    print("用例3:搜索python-3")
 
if __name__ == "__main__":
    pytest.main(["-s", "test_fixturemodule2.py"])

运行结果:

实例3

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest
 
@pytest.fixture(scope="session")
def open():
    # 会话前置操作setup
    print("===打开浏览器open===")
    yield
    # 会话后置操作teardown
    print("===关闭浏览器open===")
 
@pytest.fixture
def login(open):
    # 方法级别前置操作setup
    print("===登陆操作login===")
    name = "===账号==="
    pwd = "===密码==="
    # 返回变量
    yield name, pwd
    # 方法级别后置操作teardown
    print("===登录成功login===")
 
def test_case1(login):
    print("===测试用例1===")
    # 返回的是一个元组
    print(login)
    # 分别赋值给不同变量
    name, pwd = login
    print(name, pwd)
    assert "账号" in name
    assert "密码" in pwd
 
def test_case2(login):
    print("===测试用例2===")
    print(login)

运行结果:

yield遇到异常

1、如果其中一个用例在执行时出现异常,不影响yield后面的teardown执行,运行结果互不影响,并且全部用例执行完之后,yield唤起teardown操作。

# 新建一个文件test_f1.py
# coding:utf-8
import pytest


@pytest.fixture(scope="module")
def open():
    print("打开浏览器,并且打开百度首页")
    yield
    print("执行teardown!")
    print("最后关闭浏览器")

def test_s1(open):
    print("用例1:搜索python-1")

    # 如果第一个用例异常了,不影响其他的用例执行
    raise NameError  # 模拟异常

def test_s2(open):
    print("用例2:搜索python-2")

def test_s3(open):
    print("用例3:搜索python-3")

if __name__ == "__main__":
    pytest.main(["-s", "test_f1.py"])

运行结果:

2、但是fixture函数如果在setup执行期间发生异常,那么pytest是不会去执行yield后面的teardown内容。

3、yield关键字也可以配合with语句使用;以下是官方文档给的案例:

# 官方文档案例
# content of test_yield2.py

import smtplib
import pytest

@pytest.fixture(scope="module")
def smtp():
    with smtplib.SMTP("smtp.gmail.com") as smtp:
        yield smtp  # provide the fixture value

yield关键字+with上下文管理器的结合使用

yield 关键字 也可以配合  with 上下文管理器 语句使用。【使得代码更加精简】

示例:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pytest
import smtplib
 
@pytest.fixture(scope="module")
def smtp_connection():
    with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
        yield smtp_connection

 

request.addfinalizer()将定义的函数注册为终结函数

除了yield可以实现teardown,我们也可以通过 request.addfinalizer() 的方式去注册终结函数来实现 teardown 用例的后置操作。

示例:

addfinalizer 的用法跟 yield 是不同的, addfinalizer 需要你去注册一个或多个作为终结器使用的函数

例如:增加一个函数 fin,并且注册成终结函数

代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pytest
 
@pytest.fixture(scope="module")
def test_addfinalizer(request):
    # 前置操作setup
    print("===打开浏览器===")
    test = "test_addfinalizer"
 
    def fin():
        # 后置操作teardown
        print("===关闭浏览器===")
 
    request.addfinalizer(fin)
    # 返回前置操作的变量
    return test
 
def test_case(test_addfinalizer):
    print("===最新用例===", test_addfinalizer)

运行结果:

yield 与 addfinalizer 用法的区别:

① addfinalizer 可以注册多个终结函数。当注册多个终结函数时,用例的后置操作同时会执行完所有的终结函数。

【注意】终结函数(用例后置操作函数)的执行顺序与其在fixture函数中注册的顺序相反(即先注册的终结函数后执行,后注册的终结函数先执行)

示例:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pytest


@pytest.fixture()
def demo_addfinalizer(request):
    print("====setup====")

    def fin1():
        print("====teardown1====")

    def fin2():
        print("====teardown2====")

    def fin3():
        print("====teardown3====")

    # 注册fin1、fin2、fin3为终结函数
    request.addfinalizer(fin1)
    request.addfinalizer(fin2)
    request.addfinalizer(fin3)


def test_case1(demo_addfinalizer):
    print("====执行用例test_case1====")


def test_case2(demo_addfinalizer):
    print("====执行用例test_case2====")


def test_case3(demo_addfinalizer):
    print("====执行用例test_case3====")


if __name__ == '__main__':
    pytest.main(__file__, '-s')

运行结果:

②当执行测试用例时setup前置操作函数的代码执行错误或者发生异常时,addfinalizer 注册的终结函数依旧会执行。

③ yield 关键字可以返回setup前置操作函数中生成的测试数据,且 yield 关键字返回测试数据之后后续的代码依然可以运行。且后续执行的代码充当teardown后置操作函数。

④ addfinalizer 函数可以将一个或者多个函数注册为终结函数(一个或多个函数必须在fixture函数中定义),此时的终结函数为teardown后置操作函数;且最后可以使用 return 关键字返回setup前置操作函数生成的测试数据。

posted @ 2021-07-12 18:25  习久性成  阅读(230)  评论(0编辑  收藏  举报