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

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"])

运行结果:

image

【注意】: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"])

运行结果:

image

实例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)

运行结果:

image

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"])

运行结果:

image

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

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)

运行结果:

image

yieldaddfinalizer 用法的区别:

① 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')

运行结果:

image

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

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

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

posted @ 2022-08-05 10:46  观棋不雨  阅读(173)  评论(0编辑  收藏  举报