Python技术迷

robotframework,一个有趣的 Python 库!

Robot Framework 这个库,有点像给测试脚本装了个翻译器

接口回归又挂了。

日志里就一行:

order_status expect=PAID actual=INIT trace_id=pay_20240629_8f31

这种问题我一般不急着翻业务代码,先看测试脚本。很多项目的自动化测试写到后面,会变成一堆 requests.post()、assert、sleep,再夹几个环境变量。刚开始还行,半年后谁碰谁烦。

Robot Framework 有意思的地方就在这。

它不是让你少写测试,而是把“测试步骤”从 Python 代码里拆出来。业务同学能看懂步骤,测试同学能改用例,底下真正干活的关键字,还是我们用 Python 写。

Robot Framework 是一个开源自动化框架,常用于测试自动化和 RPA,官方也把它描述成基于关键字驱动、可扩展的自动化框架。它本身可以通过 Python 扩展自定义库,安装也就是常规的 pip install robotframework。现在 PyPI 上的说明里也写了,它需要 Python 3.8 或更新版本。

先装:

pip install robotframework

我不太喜欢一上来就写登录百度、打开浏览器那种 demo。太虚。拿一个更像现场的接口回归来说:创建订单、模拟支付回调、查订单状态。

目录大概这样:

case_demo/
  libs/
    order_keywords.py
  tests/
    order_flow.robot

Python 关键字库我一般会写得短一点,别搞成框架套框架:

# libs/order_keywords.py
import time
import requests

classOrderKeywords:
def__init__(self):
        self.host = "http://127.0.0.1:8080"
        self.last_order_id = None

defcreate_order_for_user(self, user_id: str, amount: int):
        payload = {
"userId": user_id,
"amount": amount,
"source": "robot_regression"
        }

        r = requests.post(
f"{self.host}/api/orders",
            json=payload,
            timeout=3
        )

if r.status_code != 200:
raise AssertionError(f"create order failed, code={r.status_code}, body={r.text}")

        body = r.json()
        self.last_order_id = body.get("orderId")

ifnot self.last_order_id:
raise AssertionError(f"orderId missing, response={body}")

return self.last_order_id

defmock_pay_callback(self):
ifnot self.last_order_id:
raise AssertionError("last_order_id is empty, create order first")

        r = requests.post(
f"{self.host}/api/pay/callback",
            json={"orderId": self.last_order_id, "payChannel": "mock"},
            timeout=3
        )

if r.status_code != 200:
raise AssertionError(f"pay callback failed, body={r.text}")

deforder_status_should_be(self, expect_status: str):
        deadline = time.time() + 5

while time.time() < deadline:
            r = requests.get(
f"{self.host}/api/orders/{self.last_order_id}",
                timeout=2
            )
            body = r.json()
            actual = body.get("status")

if actual == expect_status:
return

            time.sleep(0.5)

raise AssertionError(
f"order_status expect={expect_status} actual={actual} order_id={self.last_order_id}"
        )

这个类里的方法名,Robot Framework 会自动当成关键字用。比如 create_order_for_user,在 .robot 文件里就可以写成 Create Order For User。

测试用例长这样:

*** Settings ***
Library    ../libs/order_keywords.py

*** Test Cases ***
订单支付后状态应该变成已支付
    ${order_id}=    Create Order For User    user_10086    59
    Mock Pay Callback
    Order Status Should Be    PAID

跑一下:

robot -d reports tests/order_flow.robot

跑完以后,它会生成报告。

这里我觉得 Robot Framework 比较舒服的一点,不是语法多高级,而是报告够直接。哪个步骤挂了,参数是什么,错误信息是什么,基本能顺着看下去。

比如上面的状态没变,它抛出来的不是冷冰冰的 False is not true,而是这句:

order_status expect=PAID actual=INIT order_id=O202406290012

这个错误信息是自己写的。别小看这点。

线上排障很多时间不是花在修 bug 上,是花在“这玩意儿到底挂在哪一步”上。自动化测试也一样。脚本写得再多,失败日志看不懂,最后还是靠人肉点页面。

Robot Framework 还有一个地方挺适合团队协作:用例文件不用塞太多技术细节。

像这个:

订单支付后状态应该变成已支付
    Create Order For User    user_10086    59
    Mock Pay Callback
    Order Status Should Be    PAID

产品、测试、后端都能看懂。真正怎么发请求、怎么等状态、怎么处理超时,藏在 Python 里。

不过这库也不是万能的。

我见过有人把 Robot Framework 写成另一种“屎山”:一个关键字里面再调十几个关键字,变量满天飞,最后比 Python 还难读。

我的习惯是,Robot 文件只写业务步骤,Python 文件只写稳定动作。不要在 Robot 里写太复杂的判断,也不要把 Python 关键字拆得像积木碎片。

比如这种我就不太信:

Run Keyword If    '${status}' == 'INIT'    Sleep    3s
Run Keyword If    '${status}' == 'INIT'    Query Again
Run Keyword If    '${status}' != 'PAID'    Fail

这种逻辑放 Robot 里,刚开始看着灵活,后面就是灾难。等待、重试、异常兜底,老老实实放 Python 里。

Robot Framework 适合什么?

适合接口回归、冒烟测试、业务流程验收、跨系统联调检查。尤其是那种“步骤很业务,但动作很固定”的场景。

不太适合什么?

不太适合拿来写很细的单元测试,也不适合强行替代 pytest。单元测试该用 pytest 还是用 pytest。Robot Framework 更像是站在业务流程上往下压一层。

我的判断很简单:如果你的测试脚本已经开始出现大量重复请求、重复登录、重复断言,而且失败报告没人愿意看,可以试试 Robot Framework。

它不神,但挺实用。

尤其是配上 Python 自定义关键字以后,它就不是一个“测试人员专用工具”,而是一个能把接口回归、业务验收、排障脚本揉在一起的自动化壳子。

别把它写复杂,就挺好用。