API 接口自动化测试详细图文教程学习系列23--结合Pytest框架使用4-前后置处理

测试学习记录,仅供参考!

Pytest 框架

本文实际结合测试项目接口服务进行 API 接口自动化测试;

建议可参考学习:使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 19--测试框架Pytest基础 3--前后置操作应用

九、前后置处理

在测试框架中,前后置是指在执行测试用例前和执行测试用例后一些额外的操作,这些操作可以用于设置测试环境、准备测试数据等,以确保测试的可靠性和一致性。在 pytest 框架中提供了三种前后置操作的处理方式。

setup、teardown、setup_class、teardown_class 等等

第一种方式在 pytest 框架中并不常用,一般很少这种方式,setup、teardown函数级别的前后置,在每个测试函数开始和结束时都会执行一次前后置操作,用于函数级别的初始化和清理。

1、 新建测试文件,在 login 目录下新建 test_setup_teardown.py 文件,并输入以下内容;

  • 引入import pytest;

  • 定义一个测试类TestSetup;

  • 创建测试函数方法,其中测试函数级别的前置处理操作是固定写法setup,其他自定义即可;

  • 主函数测试运行查看结果;

    导包

    import pytest

    定义一个测试类

    class TestSetup:

    复制代码
      # 测试函数级别的前置处理操作--固定写法
      def setup(self):
          print('所有测试函数执行前先执行这个操作')
    
      def test_case_01(self):
          print('这是第一个测试用例')
    
      def test_case_02(self):
          print('这是第二个测试用例')
    
      def test_case_03(self):
          print('这是第三个测试用例')
    
      # 测试函数级别的后置处理操作--固定写法
      def teardown(self):
          print('所有测试函数执行结束后先执行这个操作')

    if name == 'main':
    pytest.main(['-k', 'test_setup_teardown'])

执行查看测试结果,能够发现所有测试函数执行前先执行前置处理操作;

setup_class、teardown_class 类级别的前后置,在每个测试类开始和结束时只执行一次前后置,用于类级别的初始化和清理;

  • 继续添加后置处理操作teardown_class;

    导包

    import pytest

    定义一个测试类

    class TestSetup:

    复制代码
      def setup_class(self):
          print('在测试类执行前先执行此方法')
    
      def test_case_01(self):
          print('这是第一个测试用例')
    
      def test_case_02(self):
          print('这是第二个测试用例')
    
      def test_case_03(self):
          print('这是第三个测试用例')
    
      def teardown_class(self):
          print('在测试类执行结束后先执行此方法')

    if name == 'main':
    pytest.main(['-k', 'test_setup_teardown'])

测试运行,从结果中能够看到所有测试函数执行前后均会先执行前置、后置处理操作;

补充:

pytest提供了以下5个前置后置方法

  1. setup_module、teardown_module(模块级别):整个py文件,测试用例执行前后只执行了一次;
  2. setup_class、teardown_class:(类级别):整个测试类中,测试用例执行前后只执行了一次;
  3. setup、teardown(函数级别) 作用于类中/类外测试函数 :每条用例执行前后都会执行一次,既可以作用于类中测试函数,亦可以作用于类外测试函数;
  4. setup_method、teardown_method(方法级别):每条用例执行前后都会执行一次,只作用于类中测试函数;
  5. setup_function、teardown_function(函数级别):每条用例执行前后都会执行一次,只作用于类外测试函数使用;
总结:
作用域

测试类中:setup_class、teardown_class,setup_method、teardown_method;

测试类外:setup_function、teardown_function,setup_module、teardown_module;

测试类中、类外均可:setup、teardown;

执行顺序

类中前置执行顺序:setup_class > setup_method > setup (后置执行顺序则相反);

类外前置执行顺序:setup_module > setup_function > setup (后置执行顺序则相反);

运行优先级
  1. 模块级别(setup_module / teardown_module)模块始末,全局的(优先级别最高);
  2. 函数级(setup_function / teardown_function)只对函数用例生效 (不在类中);
  3. 类级 (setup_class / teardown_class)只在类中前后运行一次 (在类中);
  4. 方法级 (setup_method / teardown_method)开始于方法始末 (在类中);
  5. 类里面 (setup / teardown)运行在调用方法的前后;
  • setup_function 和 teardown_function: 用于每个测试函数;
  • setup_method 和 teardown_method: 用于每个测试方法(类级别);
  • setup_module 和 teardown_module: 用于每个模块;
  • setup_class 和 teardown_class: 用于每个类;
  • setup 和 teardown: 用于每个测试用例(一般通过fixture实现);

通过 @pytest.fixture 来实现pytest前后置应用

在pytest框架中用于定义和使用fixture(装置、夹具、脚手架)的装饰器,fixture可以用于在测试函数执行前后进行一些初始化和清理操作,以及共享状态和资源;需要灵活掌握;

第一,首先需要先定义fixture(不带参数):使用@pytest.fixture装饰器定义一个fixture函数,fixture函数可以包含初始化等逻辑;

2、 在 login 目录下新建 test_fixture.py 文件,并输入以下内容;在最外层先定义一个夹具,夹具名称可以自定义;(若没有加上 @pytest.fixture 时,它只是一个普通的函数,当加上 @pytest.fixture 后,就表示此函数是一个前置应用了);

复制代码
# 导包
import pytest

# 不带参数的fixture夹具
@pytest.fixture
def set_and_teardown():
    print('测试函数执行前先执行前置操作!')

# 定义一个测试类
class TestFixture:

    def test_case_01(self):
        print('这是第一个测试用例')

    def test_case_02(self):
        print('这是第二个测试用例')

    def test_case_03(self):
        print('这是第三个测试用例')

if __name__ == '__main__':
    pytest.main(['-k', 'test_fixture'])

第二,在测试函数使用fixture:使用测试函数的参数来接收fixture的返回值,这样测试函数就可以在执行前后使用fixture提供的资源或状态。

3、修改 test_fixture.py 文件;若要在测试用例中需要使用到夹具,则在测试函数的参数通过夹具名称,这样此测试函数就使用到夹具了;接收时,参数名务必跟夹具名称一致,它才能去匹配到;

复制代码
# 导包
import pytest

# 不带参数的fixture夹具
@pytest.fixture
def set_and_teardown():
    print('测试函数执行前先执行前置操作!')


# 定义一个测试类
class TestFixture:
    # 若要在测试用例中需要使用到夹具--则在测试函数的参数通过夹具名称--这样此测试函数就使用到夹具了
    def test_case_01(self, set_and_teardown):
        print('这是第一个测试用例')

    def test_case_02(self):
        print('这是第二个测试用例')
    # 接收时,参数名务必跟夹具名称一致,它才能去匹配到
    def test_case_03(self, set_and_teardown):
        print('这是第三个测试用例')

if __name__ == '__main__':
    pytest.main(['-k', 'test_fixture'])

第三,执行顺序:pytest会自动检测 测试函数中的参数,如果发现参数的名称与fixture的名称匹配,就会将fixture注入到测试函数中,执行顺序为先执行fixture的前置应用代码,然后再去执行测试函数,最后执行fixture的后置应用代码。

第一种方式:通过函数嵌套,将函数注册为最终器实现前后置应用,比较繁琐困难,并不推荐;

4、修改 test_fixture.py 文件;

复制代码
# 导包
import pytest

# 不带参数的fixture夹具
@pytest.fixture
def set_and_teardown(request):
    print('前置操作:初始化数据,清理上次运行测试结果!')
    # 前置应用代码写好后--再来写一个后置--通过前置set_and_teardown(request)传一个参数request--这个参数名是固定的,通过夹具的话就是要这个参数
    def teardown():
        print('后置操作:清理测试数据')
    # 写完之后这里此时就相当于 函数里面嵌套了一个函数 --这种情况需要怎么办呢?--
    # 通过传进来的这个参数 request 把这个函数注册为一个最终器--通过request.addfinalizer(teardown)把 后置应用teardown 注册为最终器
    request.addfinalizer(teardown)  # 将 后置应用 teardown 函数 注册为最终器,以确保在测试用例运行结束后执行后置操作
    # 最后return一下--把这个函数名 teardown 给返回出去--这样就是一个完整的前后置应用了
    return teardown

# 定义一个测试类
class TestFixture:
    # 若要在测试用例中需要使用到夹具--则在测试函数的参数通过夹具名称--这样此测试函数就使用到夹具了
    def test_case_01(self, set_and_teardown):
        print('这是第一个测试用例')

    def test_case_02(self):
        print('这是第二个测试用例')
    # 接收时,参数名务必跟夹具名称一致,它才能去匹配到
    def test_case_03(self, set_and_teardown):
        print('这是第三个测试用例')

if __name__ == '__main__':
    pytest.main(['-k', 'test_fixture'])
第二种方式:通过 yield 关键词实现前后置应用,比较简单,推荐使用第二种方式;

5、修改 test_fixture.py 文件;

复制代码
# 导包
import pytest

# 不带参数的fixture夹具
@pytest.fixture
def set_and_teardown():
    print('前置操作:初始化数据,清理上次运行测试结果!')
    yield
    print('后置操作:清理测试数据')

# 定义一个测试类
class TestFixture:
    # 若要在测试用例中需要使用到夹具--则在测试函数的参数通过夹具名称--这样此测试函数就使用到夹具了
    def test_case_01(self, set_and_teardown):
        print('这是第一个测试用例')

    def test_case_02(self):
        print('这是第二个测试用例')
    # 接收时,参数名务必跟夹具名称一致,它才能去匹配到
    def test_case_03(self, set_and_teardown):
        print('这是第三个测试用例')

if __name__ == '__main__':
    pytest.main(['-k', 'test_fixture'])

yield 关键词,当函数有 yield 时,就相当于是一个 生成器,不是一个普通的函数了;在pytest中,通过使用yield的话,它就是在pytest里面的 fixture 里面,这个yield的作用是将夹具里面的初始化部分和后置的应用分隔开,然后并在测试函数前后分别去执行前置应用、后置应用,简单理解就是每次执行时,执行前会先执行前置,然后在此处暂停,暂停之后再去把 测试用例去执行完之后,然后去调用yield下面的这一部分代码。它就是在上一次执行的位置给暂停,下一次执行的时候,再接着继续上一次执行的结果。

第四,定义fixture(带参数):使用@pytest.fixture装饰器定义一个fixture函数,可携带参数 @pytest.fixture(scope='',autouse='',params='',ids='',name='')(先默认传为空值);

scope:控制fixture的作用范围,可选值有:"function"、"class"、"session"、"module",只能选这四个当中的一个;

第一个可选值:测试函数级别 function :默认(默认情况可以不写这个参数),每个测试函数执行前后都会运行一次前后置;

第二个可选值:类级别 class :控制单个文件每个类,在每个测试类执行前只运行一次前后置;

测试函数级别function和类级别class是单个的作用范围;

第三个可选值:模块级别 module :控制全局,每个模块(即每个.py)执行前后运行一次;

第四个可选值:会话级别 session :控制全局,整个pytest会话(多个模块)执行前后运行一次;

模块级别module和会话级别session是全局的作用范围;

autouse:用于指定是否自动应用fixture而无需在测试函数中显示的调用,默认为False;

params:用于参数化的一个选项,它允许为fixture定义多个参数值,以便在测试函数中使用不同的参数组合允许测试;

ids:为参数化的fixture提供自定义的标识,用于在测试报告中更加清晰显示参数,需要跟params参数结合起来一起使用;

name:自定义fixture的名称;

6、修改 test_fixture.py 文件;完善内容,把不带参数的fixture夹具和带参数的fixture 夹具代码完善;

复制代码
# 导包
import pytest

# 不带参数的fixture夹具
@pytest.fixture
def set_and_teardown():
    print('前置操作:初始化数据,清理上次运行测试结果!')
    yield
    print('后置操作:清理测试数据')


# 带参数的fixture
@pytest.fixture(autouse=True, params=['北京', '城都', '重庆'], ids=['BJ', 'CD', 'CQ'], name='setValue')
def set_params(request):
    return request.param

# 定义一个测试类
class TestFixture:

    def test_case_01(self, setValue):
        print('这是第一个测试用例')
        print(f'获取到的值为:{setValue}')

    def test_case_02(self):
        print('这是第二个测试用例')

    def test_case_03(self, set_and_teardown):
        print('这是第三个测试用例')

if __name__ == '__main__':
    pytest.main(['-k', 'test_fixture'])
第三种方式:使用 @pytest.fixture 和 conftest.py 结合使用实现一个全局的前后置应用

@pytest.fixture与conftest.py文件结合使用,可以实现在多个测试模块(.py)中共享前后置应用操作,这种结合的方式使得可以在整个测试项目中定义和维护通用的前后置逻辑,使测试代码更加模块化和可维护。

规则:

  • conftest.py 是一个单独存放的夹具配置文件,名称是固定的写法,不能更改;
  • 可以在项目中的不同目录创建多个conftest.py,每个conftest.py文件都会对其所在的目录及其子目录下的测试模块生效;
  • 在不同模块的测试中需要用到conftest.py的前后置功能时,不需要做任何的导入操作,可以直接在测试函数的参数中使用fixture名称;
  • 作用:可以在不同的py文件中使用同一个fixture函数;

7、 在 testcases 目录下新建 conftest.py 文件,并输入以下内容,里面写前后置操作;

复制代码
# 导包
import pytest
# 前后置操作
@pytest.fixture(scope='function', autouse=True)
def print_info():
    print('----------接口开始测试----------')
    yield
    print('----------接口测试结束----------')

8、修改 test_parameter.py 文件内容,注意里面的文件路径,是以当前运行的文件开始去查找;此时在主函数里面去执行,运行 run.py 文件,能够执行成功,可以看到每个测试函数级的用例都有前后置操作。

复制代码
# 导包
import pytest
from unit_tools.handle_data.yaml_handler import read_yaml
# 定义一个测试类
class TestParameter:
    # 传递多个参数,通过列表类型的可迭代对象实现参数化
    @pytest.mark.parametrize('user_name, password', [('test01', 'admin123'), ('test01', 'admin999'), ('test02', 'admin123'), ('test@#&', 'admin999$-~')])
    def test_login(self, user_name, password):
        print(user_name, password)
        print('正确的用户名和密码登录成功校验')

    # 传递一个参数
    @pytest.mark.parametrize('user_name', ['test01', 'test02', 'test03'])
    def test_login02(self, user_name):
        print(user_name)
        print('这是要打印的内容:test_login02')

    # 通过元组类型的可迭代对象实现参数化
    @pytest.mark.parametrize('user_name', ('test01', 'test02', 'test03'))
    def test_login03(self, user_name):
        print(user_name)
        print('这是一个打印内容test_login03-元组类型')

    # 通过字典类型的可迭代对象实现参数化--字典类型的是迭代其key值
    @pytest.mark.parametrize('user_name', {"user_name": "test01", "user_name2": "test02"})
    def test_login04(self, user_name):
        print(user_name)
        print('这是一个打印内容test_login04-字典类型')

    # 引入读取yaml文件方法--这里需要注意文件的 相对路径 位置
    @pytest.mark.parametrize('api_info', read_yaml('./datas/login.yaml'))
    def test_login05(self, api_info):
        print(f"获取到的接口信息:{api_info}")

if __name__ == '__main__':
    pytest.main(['-k', 'test_parameter'])

未完待续。。。

相关推荐
USC-XiangLuXun1 小时前
局部科技小创新是有意义的
科技·学习·生活
weixin_BYSJ19871 小时前
springboot旅游管理系统04470(附源码+开发文档+部署教程)
java·spring boot·python·算法·django·flask·旅游
Upsy-Daisy1 小时前
IOTA 学习笔记(三):IOTA 的技术演进路线
笔记·学习
有个人神神叨叨1 小时前
Agent Memory 学习笔记-1.0
笔记·学习
kaico20181 小时前
Python 在 Jenkins Pipeline 中的使用总结
开发语言·python·jenkins
多彩电脑1 小时前
在Kivy中制造可移动控件
python
Zy_Yin1232 小时前
拆解如何用anthropic金融agent做投研
人工智能·python·深度学习·金融·github
清水白石0082 小时前
Python 变量的本质:从“盒子思维”到“引用思维”,彻底理解赋值到底发生了什么
java·python·ajax
yaoxin5211232 小时前
423. Java 日期时间 API - DayOfWeek 和 Month 枚举
开发语言·python