Pytest-Bdd-Playwright 系列教程(12):步骤参数 & parsers参数解析

Pytest-Bdd-Playwright 系列教程(12):步骤参数 & parsers参数解析

  • 前言
  • 一、什么是步骤参数?
  • [二、pytest-bdd 的步骤参数用法](#二、pytest-bdd 的步骤参数用法)
    • [2.1 简单字符串解析](#2.1 简单字符串解析)
    • [2.2 自定义正则表达式解析](#2.2 自定义正则表达式解析)
    • [2.3 参数类型转换](#2.3 参数类型转换)
  • [三、案例:基于 pytest-bdd 实现计算器功能测试](#三、案例:基于 pytest-bdd 实现计算器功能测试)
    • [3.1. 编写 Feature 文件](#3.1. 编写 Feature 文件)
    • [3.2 实现步骤定义](#3.2 实现步骤定义)
    • [3.3 执行测试](#3.3 执行测试)
  • 四、最佳实践
  • 总结

前言

  • 在 pytest-bdd 中,步骤参数是构建动态、灵活测试用例的核心功能之一,它允许通过占位符的形式将具体值插入步骤中,从而避免重复编写相似的场景;
  • 我们可以通过给步骤添加参数来重用步骤,实现单一实现和多重使用,从而使代码更简洁;
  • 本文将探讨如何通过简单字符串解析、自定义正则表达式解析以及参数类型转换等方法来灵活处理不同的参数需求,并通过一个基于计算器功能的完整案例进行说明。

一、什么是步骤参数?

在 Gherkin 描述中,步骤参数(Step Parameters)是动态定义测试数据的关键方式。它允许通过占位符的形式将具体值插入步骤中,从而避免重复编写相似的场景。例如:

gherkin 复制代码
Given 第一个数字是 5
And 第二个数字是 3
When 我按下 加号
Then 结果应该是 8

上述例子中的 538 是参数,通过步骤实现函数将它们传递到测试逻辑中进行处理。

二、pytest-bdd 的步骤参数用法

在 pytest-bdd 中,步骤参数通过字符串匹配实现,配合 pytest-bdd.parsers 模块,可以实现灵活的参数解析。以下是几种常见的参数解析方式:

2.1 简单字符串解析

简单字符串解析是 pytest-bdd 的默认行为,可以直接使用占位符解析参数。例如:

gherkin 复制代码
Given 第一个数字是 <first_number>

对应的 Python 实现:

python 复制代码
from pytest_bdd import given, parsers

@given(parsers.parse("第一个数字是 {first_number:d}"))
def first_number(first_number):
    return first_number

解析规则通过 {} 语法实现,其中 :d 表示参数是整数类型。

2.2 自定义正则表达式解析

如果需要更复杂的匹配逻辑,可以使用自定义正则表达式。例如:

gherkin 复制代码
Then 计算器的宽度应该是 12.5

实现代码:

python 复制代码
@then(parsers.re(r"计算器的宽度应该是 (?P<expected_width>\d+\.\d+)"))
def check_width(expected_width):
    assert float(expected_width) == 12.5

通过 parsers.re() 定义正则表达式,可以精确控制参数提取规则。

2.3 参数类型转换

pytest-bdd 支持内置的数据类型转换,例如:

  • {name:s}:字符串
  • {value:d}:整数
  • {value:f}:浮点数

在示例中,我们可以指定参数类型:

gherkin 复制代码
Given 第一个数字是 5

对应 Python 代码:

python 复制代码
@given(parsers.parse("第一个数字是 {first_number:d}"))
def first_number(first_number):
    return first_number

测试框架会自动将参数 first_number 转换为整数。


三、案例:基于 pytest-bdd 实现计算器功能测试

接下来,我们通过一个具体的案例,展示如何在 pytest-bdd 中使用步骤参数。

3.1. 编写 Feature 文件

首先,新增 featuress/calculator_example.feature文件,定义计算器的几个功能测试场景:

gherkin 复制代码
Feature: 计算器
  一个简单的计算器,用于执行基本的算术操作。

  Background:
    Given 我已经准备好计算器

  Scenario: 检查计算器的尺寸
    Then 计算器的宽度应该是 12.5
    And 计算器的高度应该是 20.0
    And 计算器的厚度应该是 0.5

  Scenario: 打开计算器
    Given 我按下电源按钮
    Then 屏幕应该亮起

  Scenario Outline: 两个数之间的计算
    Given 我检查按钮是否正常
    And 第一个数字是 <first_number>
    And 第二个数字是 <second_number>
    When 我按下 <operation>
    Then 结果应该是 <expected_result>

    Examples:
      | first_number | second_number | operation | expected_result |
      | 5            | 3            | 加号       | 8               |
      | 10           | 4            | 减号       | 6               |
      | 2            | 6            | 乘号       | 12              |
      | 8            | 2            | 除号       | 4               |

3.2 实现步骤定义

tests/test_calculator_example.py 文件中为上述场景定义步骤参数:

python 复制代码
import pytest
from pytest_bdd import given, when, then, parsers, scenarios


scenarios("calculator_example.feature")

@given("我已经准备好计算器")
def _():
    print("计算器已准备好!")

@given("我检查按钮是否正常")
def _():
    print("按钮已检查。")

@given("我按下电源按钮")
def _():
    pass

@then("屏幕应该亮起")
def _():
    pass

@then(parsers.parse("计算器的宽度应该是 {expected_width:f}"))
def _(expected_width: float):
    print(f"宽度: {expected_width}")

@then(parsers.parse("计算器的高度应该是 {expected_height:f}"))
def _(expected_height: float):
    print(f"高度: {expected_height}")

@then(parsers.parse("计算器的厚度应该是 {expected_thickness:f}"))
def _(expected_thickness: float):
    print(f"厚度: {expected_thickness}")

@given(parsers.parse("第一个数字是 {first_number:d}"), target_fixture="first_number")
def _(first_number):
    return first_number

@given(parsers.parse("第二个数字是 {second_number:d}"), target_fixture="second_number")
def _(second_number):
    return second_number

@when(parsers.parse("我按下 {operation}"), target_fixture="result")
def _(operation, first_number, second_number):
    if operation == "加号":
        return first_number + second_number
    elif operation == "减号":
        return first_number - second_number
    elif operation == "乘号":
        return first_number * second_number
    elif operation == "除号":
        return first_number / second_number
    else:
        raise ValueError(f"不支持的操作: {operation}")

@then(parsers.parse("结果应该是 {expected_result:d}"))
def _(result, expected_result):
    assert result == expected_result

3.3 执行测试

运行测试命令:

bash 复制代码
pytest .\tests\test_calculator_example.py

输出结果如下:

四、最佳实践

  1. 合理使用参数类型

    根据步骤的需求选择合适的数据类型解析器,确保输入值正确解析。

  2. 避免重复代码

    如果多个步骤有相似逻辑,可以使用 Python 的函数装饰器和参数化技术优化代码。

  3. 使用目标 Fixture

    利用 target_fixture 提取步骤中的数据,简化上下文传递。

  4. 测试数据分离

    将测试数据与逻辑分离(如使用 Examples 表),提高测试用例的可读性和可维护性。

总结

pytest-bdd 的步骤参数功能通过灵活的解析机制,为测试用例的动态数据支持提供了强大的工具。在本文中,我们通过对步骤参数的详尽讲解和计算器案例的实操演示,展示了如何高效使用这一功能。

相关推荐
姚青&6 小时前
Pytest 测试用例编写
测试用例·pytest
Warren987 小时前
Pytest Fixture 作用域与接口测试 Token 污染问题实战解析
功能测试·面试·单元测试·集成测试·pytest·postman·模块测试
测试秃头怪20 小时前
面试大厂就靠这份软件测试八股文了【含答案】
自动化测试·软件测试·python·功能测试·面试·职场和发展·单元测试
测试杂货铺20 小时前
软件测试面试题大全,你要的都在这。。
自动化测试·软件测试·python·功能测试·面试·职场和发展·测试用例
程序员小远1 天前
UI自动化测试用例管理平台搭建
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
serve the people1 天前
python环境搭建 (七) pytest、pytest-asyncio、pytest-cov 试生态的核心组合
开发语言·python·pytest
真智AI2 天前
用 LLM 辅助生成可跑的 Python 单元测试:pytest + coverage 覆盖率报告(含运行指令与排坑)
python·单元测试·pytest
Warren982 天前
Allure 常用装饰器:实战用法 + 最佳实践(接口自动化)
运维·服务器·git·python·单元测试·自动化·pytest
feasibility.3 天前
playwright爬虫采集京东商品主页数据(含xpath定位示例)
爬虫·playwright