Pytest自动化框架搭建

背景

入职了一家小公司,需要从0开始搭建一个自动化测试环境,因为是测试板卡项目,所以需要使用串口连接工具pyserial,测试用例批量化执行工具pytest,测试报告自动生成工具allure,大家根据这三个关键词选择是否需要继续阅读

1. 技术实现

1.1 技术说明

  • 使用pyserial作为串口连接工具,发送读写命令;
  • 使用pytest作为用例批量管理工具;
  • 使用allure对测试结果进行可视化处理,自动生成测试报告

1.2 后续拓展

  • 持续集成:构建冒烟和全量用例,支撑每次版本发布
  • 结果呈现:自动处理测试结果,生成表格并邮件自动抄送相关人员

2. 目录结构

2.1 testcases(文件夹)

用来存放测试脚本测试夹具,例如:test_demo.py、conftest.py等

2.1.1 子文件夹分类

同时存在两种分类的维度,第一种按照功能性能可靠性等进行分类,支撑单轮验证

  • function:功能测试
  • performance:性能测试
  • reliability:可靠性测试

第二种按照冒烟和全量进行分类,支撑版本迭代

  • smoke:冒烟测试(少量基本功能用例,支撑版本可用性验证)
  • full:全量测试(所有测试用例,支撑版本迭代验证)

2.1.2 test_demo.py

需要实现的测试脚本,原则上该脚本内不定义任何功能函数,全部从function文件夹下导入。内部定义详细的测试类测试方法,脚本的实现步骤与实际测试用例的描述步骤必须保证完全一致!!!

#导入function文件夹下定义的函数
from function.serial port,link serial port import open com

#此处为测试类的定义,例如:网口自协商类,板卡复位类
class TestCommunicateserialPort:
    #此处为测试方法的定义,例如:分别配置两个网口,检查自协商结果与预期是否一致
    def test_case0l_a_connect_b(self):
        # step 1
        do something1()
        # step 2
        do something2()
        # 判断结果
        assert expected result == actual result

2.1.3 conftest.py

需要实现的测试夹具,定义部分/全部用例的前后置执行内容,例如:连接串口(前置),关闭串口(后置)

# 装饰器,参数为该装饰器的作用范围
@pytest.fixture(scope='function', autouse=True)
def communicate_serial_port():
    do something()
    yield
    time.sleep(5)
  • scope:被装饰器标记的作用域,可选参数有function(默认),class,module,package/session
  • params:参数化,可往函数内传参,支持数据类型列表[],元组(),字典列表[{},{}],字典元组({}),{})
  • autouse:是否使用该装饰器,默认为False(不使用)

2.2 function(文件夹)

用来存放通用的功能函数,例如:connect_serial_port.py等,在testcases目录下的测试脚本可直接import调用

2.2.1 子文件夹分类

按照实际功能模块来划分,以下示例仅为参考

  • network:网口配置相关
  • serial_port:串口连接,串口配置相关
  • reset:网口复位,板卡复位相关

2.2.2 connect serial port.py

该函数仅用于打开串口并判断串口是否打开

def open serial port(n: int):
    ports = list(serial.tools.list ports.comports(include_links=False))
    ser = serial.serial(port=ans[0], baudrate=230400, parity=serial.PARITY_ NONE, stopbits=sertimeout=1, rtscts=False)
    if ser.isopen():
        print('\nserial port connection is successful!')
    else:
        print("\nserial port connection is failure!')
        assert ser.isopen()
    return ser

2.3 reports(文件夹)

用来存放json格式的用例执行结果+allure生成的htmI报告

2.3.1 xml

用来存放每个用例的独立执行结果(json格式),通过pytest.ini中 addopts =--alluredir ./reports/xml/来控制

  • --alluredir:后接需要存放的报告缓存地址

2.3.2 html

用来存放所有用例的整体执行结果(html格式),通过main.py中os.system('allure generate ./reports/xml -0/reports/html/ --clean')来控制

  • generate:后接取缓存报告的地址(源地址)
  • -o:后接报告生成后的存放地址(目的地址)
  • --clean:覆盖原来的htmI报告,默认不覆盖

2.4 pytest.ini(配置文件)

main.py执行时,会读取该文件下的配置,解析出需要测试的用例范围,例如:只执行功能测试用例,失败用例rerun,是否随机执行等

[pytest]
addopts = -vs --random-order --alluredir ./reports/xml/ --reruns=2
testpaths = ./testcases/test_communicate_serial_port.py
python_files = test_*.py
python_classes = Test*
python_functions = test
  • addopts:配置基本的执行参数,例如:显示详细信息,是否随机执行,allure生成报告等
  • testpaths:需要执行的用例范围,可执行范围包含文件夹/文件/类/方法
  • python_files:需要执行的python文件命名规则,默认为test_开头
  • python_classes:需要执行的python类命名规则,默认为Test开头
  • python_functions:需要执行的python函数命名规则,默认为test开头

2.5 main.py(主文件)

pytest执行的主入口,pytest.main()调用pytest.ini的配置后执行相关测试用例,也可以发送其他命令按照顺序执行

import os
import pytest

if __main__ == '__name__':
    pytest.main()
    os.system('allure generate ./reports/xml -o ./reports/html/ --clean')

3.数据规范说明

3.1 命名规则

3.1.1 变量

变量命名遵循Python命名规则,使用小驼峰+下划线的命名规则,例如:query_port_status(查询网口状态)

3.1.2 文件/类/函数

除了下面的命名规则之外,还需做到“见名知意”,最好使用英文翻译作为名称

  • python_files:默认为test_开头
  • python_classes:默认为Test开头
  • python_functions:默认为test开头

3.1.3 注释

为了代码的可读性,应该在文件和函数头添加相应的注释说明,一些数据转换规则最好也能体现在注释中,而不是每次重读代码或查数据对应表

3.1.3.1 文件注释

文件注释包含文件名,坐着,迭代版本,创建/更新时间,文件实现的功能,有无特殊用法(如:外部传参等python_port_config.py 【参数1】~/log_path  【参数2】~/cache_path)

"""
File Name:   port config.py
Author:      KeYou
Version:     v1.1
Created:     2022/12/20
Description: Converts instructions between hexadecimal and ASCII.
Usage:       N/A

"""
3.1.3.2 函数注释

函数注释包括函数实现的功能,内部传参(分别应该传入什么参数),返回值(返回值的类型,作用)

def hex to ascii(s: str):
    """
    -function:Converts an instruction from hexadecimal to ASCII.
    -param s:Hexadecimal command to be converted, such as: '4F440006710A'
    -return:s:str,such as:b'oDlxe0\x06g\n'
    """
    return base64.b16decode(s.upper())
3.1.3.3 函数内注释

函数内的注释,阐明一些特殊情况即可,例如bit位的对应关系,无需任何函数都详细注释。

例如上述代码中的注释,直接反馈了config变量中4个bit的组成方式,包括每个bit的对应关系,增加可读性

# config(bit0-3) = force(bit3) + speed(bit1-2) + autoneg(bit0)
    config = “0000"
# 1) autoneg(bit0): on:1, off:0
    if autoneg == "on":
        config = config[:-1]+'1'
    elif autoneg == "off":
        pass
    else:
        print("Invalid input, please re-enter autoneg!")
        return False
# 2) speed(bit1-2): F:01,G:10,FG:11
    …………………………………………………………
# 3) XXXXX: XXXXX
    …………………………………………………………

3.2 函数定义

测试方法应该更具有通用性,方便重构,并且尽可能考虑所有情况,在异常case时给出相应的报错信息,便于出现问题时快速定位

3.2.1 可重构

测试方法应该更具有通用性,方便重构

3.2.2 全面性

基本的异常路径都有考虑到,并给出相应的提示信息,方便问题定位

例如以下示例中,考虑了连接串口时可能出现的两种异常case

  1. 插入了串口但未识别到任何串口设备
  2. 插入了多个串口但我们需要的串口设备并未被识别到
def open_com(n: int):
    ports = list(serial.tools.list_ports.comports(include_links=False))
    ports_list = []
    for port in ports:
        s = str(port)
        port_list.append(s[:s.find(' ')])
    # 异常case1: 串口列表是空的,提示检查串口连接是否正常
    if not port_list:
        print('No serial port is available, please check the connection!')
        return False
    # 异常case2: 其他的串口设备,不是COMXX开头
    for port in port_list:
        if port[:3] != 'COM':
            print('Wrong serial port, Please check serial ports connection!')
            return False

3.2.3 一致性

方法的实现步骤要和测试用例的步骤保持完全一致,部分执行步骤可按照实际逻辑进行调整

# 测试用例描述如下:
# 测试用例编号&名称
def test_case0l on_g ms to_on_g_ms(self): 
    # step1:打开串口
    ser = open_com(1)
    # step2:配置网口T1-1为自协商打开, 1000M速率,SM都支持
    ser.write(hex_to_ascii(port_config("T1","on","G”,“MS”)))
    # step2的预期结果:网口配置成功
    read_msg1 = ser.read(100)
    assert ascii_to_hex(str(read_msg1)) == result_query_success

    # step3:配置网口T1-2为自协商打开1000M速率,SM都支持
    ser.write(hex to ascii(port config("T2","on",“G”,“MS")))
    # step3的预期结果:网口配置成功
    read msg2 = ser.read(100)
    assert ascii_to_hex(str(read_msg2)) == result_query_success
    
    # step4:查询网口状态
    ser.write(hex to ascii(command query port status))
    port_status =ser.read(100)
    ans = analyse_result(ascii_to_hex(str(port_status)))
    step5:关闭串口(该步骤在代码中被提前,防止用例失败后串口无法关闭)
    ser.close()
    
    # step4的预期结果:网口状态符合预期
    assert ans[0][:3] == ['T1','on','up']
    assert ans[1][:3] == ['T2','on','up']

3.2.4 独立性

用例之间互不干扰,都可以分别执行,也可整体一起随机执行

【例】如3.2.3中测试用例

单个测试用例完成了串口的打开和关闭,无其他资源等待,并及时完成释放,与其他测试用例无任何关联,互不影响,整个测试集可以按照任意顺序执行

总结

本文只是基于某个特定的项目,写了一个较为简单的自动化测试框架说明,并非企业级规范标准,甚至一些英文翻译也是机翻的,不要太在意这些细节。整篇文章可以用作个人的项目搭建练手或者面试项目叙述,仅供参考!

【注】文中有一些英文字母之间的空格可能无法理解其含义,其实是复制到博客时_字符缺失,该字符空缺变成了空格,例如:port connection.py其实是port_connection.py

  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Pytest框架是一个Python自动化测试框架,它已经成为Python自动化测试必学必备的一个框架。如果你已经掌握了基本的Python基础,那么学习和搭建Pytest框架将会很有帮助。在搭建Pytest框架时,你可以参考以下步骤: 1. 首先,确保你已经安装了Pytest框架。你可以使用pip命令来安装Pytest,例如:`pip install pytest` 2. 创建一个测试目录,用于存放测试用例和相关文件。 3. 在测试目录中创建一个`conftest.py`文件,这个文件用于存放一些框架的配置和共享的fixture。 4. 在测试目录中创建测试用例文件,用于编写测试用例。测试用例文件的命名应该以`test_`开头,例如`test_example.py`。 5. 在测试用例文件中,使用`@pytest.fixture()`装饰器来定义fixture,fixture可以用于在测试用例之前或之后执行一些操作,例如准备测试数据或清理测试环境。 6. 编写测试用例,使用`def test_`开头的函数来定义测试用例。在测试用例中,可以使用Pytest提供的丰富的断言方法来验证测试结果。 7. 运行测试用例。你可以在终端窗口中进入到测试目录下,使用`pytest`命令来运行所有的测试用例。你也可以使用`pytest -m`命令来运行指定标签的测试用例,或者使用`pytest -s`命令来打印详细的运行信息。此外,你还可以使用`pytest --html`命令来生成HTML格式的测试报告,或者使用Allure来生成更加详细和美观的测试报告。 总结起来,搭建Pytest框架的步骤包括安装Pytest、创建测试目录、编写`conftest.py`文件和测试用例文件、定义fixture和编写测试用例、运行测试用例。希望这些步骤对你搭建Pytest框架有所帮助。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* *3* [pytest接口自动化测试框架搭建](https://blog.csdn.net/Yanan990830/article/details/123285116)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值