第4章 脚本基础:Renode脚本语言(Robot Framework)入门
说实话,很多做嵌入式测试的工程师一听到「脚本语言」就头大。我当年也是这样,总觉得写脚本是开发的事,测试嘛,点点按钮就行了。直到有一次,我在一个项目里需要重复做200次故障注入——手动操作到第50次时我就知道,这条路走不通了。
Renode 用的是 Robot Framework,一个关键字驱动的自动化测试框架。你不需要成为编程高手,但得理解它的核心玩法。这一章,我就带你把它摸透。
4.1 Robot Framework 到底是什么?
Robot Framework 说白了就是一个「搭积木」的工具。你把测试步骤写成一个个关键字,然后像拼乐高一样把它们组合起来。Renode 选择它,是因为它天生适合硬件仿真场景——你可以在测试用例里同时控制仿真器、检查寄存器、注入故障,还能做断言。
核心概念三件套:
- 测试用例(Test Case):一个完整的测试场景,比如「验证UART在时钟故障下能否恢复」
- 关键字(Keyword):可复用的操作单元,比如「打开仿真」、「写入寄存器」、「注入位翻转」
- 变量(Variable):存储动态数据,比如设备地址、超时时间、期望值
我习惯把测试用例想象成菜谱,关键字就是「切菜」、「热油」、「翻炒」这些动作,变量则是「盐5克」、「水200毫升」这样的配料。你想想看,是不是一下子就通了?
4.2 第一个测试用例:Hello Renode
别急着搞复杂的东西。我们先写一个最简单的用例,确认环境能跑起来。
*** Settings ***
Library Renode
*** Test Cases ***
Hello Renode Should Work
[Documentation] 验证Renode仿真器能正常启动
Start Emulation
Sleep 1s
Stop Emulation
Log Hello Renode 测试通过!
这段代码做了三件事:启动仿真、等1秒、关闭仿真。看起来简单吧?但我在项目里见过有人直接跳过这个步骤,结果折腾半天发现是环境没配好。嗯,这里要注意——永远先跑通最小用例。
我的习惯:每开始一个新项目的测试套件,我都会先写一个「空转用例」,只启动和停止仿真。这能快速排除环境问题,避免后面排查时怀疑人生。
4.3 关键字:你的积木块
关键字是 Robot Framework 的灵魂。Renode 提供了一组内置关键字,比如 Start Emulation、Write To Register、Inject Fault。但你也可以自己写关键字,把重复操作封装起来。
举个例子,我在测试一个 SPI 驱动时,每次都要做「配置SPI → 发送数据 → 检查响应」这三步。于是我就封装了一个关键字:
*** Keywords ***
SPI Transaction Should Succeed
[Arguments] ${data_to_send} ${expected_response}
Write To Register 0x40001000 ${data_to_send}
${actual}= Read From Register 0x40001004
Should Be Equal ${actual} ${expected_response}
Log SPI事务成功:发送 ${data_to_send},收到 ${actual}
这样一来,测试用例就变得非常干净:
*** Test Cases ***
Test SPI Normal Mode
SPI Transaction Should Succeed 0xA5 0xA5
Test SPI With Clock Fault
Inject Clock Glitch frequency=1000000
SPI Transaction Should Succeed 0xA5 0x00
# 这里预期失败,因为时钟被破坏了
你看,测试逻辑一目了然。我曾经在团队里推广这种写法,新人上手速度至少快了一倍。
4.4 变量:别写死,灵活点
变量用 ${} 包裹。你可以定义全局变量、用例级变量,甚至从文件读取。我最常用的是 *** Variables *** 区:
*** Variables ***
${UART_BASE_ADDR} 0x40001000
${TIMEOUT_MS} 5000
${EXPECTED_BAUD} 115200
为什么要用变量?因为硬件地址、超时时间这些值,在项目迭代中经常变。如果你写死在每个用例里,改起来就是一场噩梦。我踩过这个坑——有一次芯片改版,UART 基地址变了,我花了整整一个下午改测试脚本。从那以后,所有地址我都用变量管理。
避坑指南:变量名不要用中文,虽然 Robot Framework 支持,但 Renode 的底层日志解析可能会乱码。我曾经因为这个排查了三个小时,最后发现是变量名里的「波特率」三个字导致的。
4.5 测试用例的组织结构
一个完整的测试套件通常长这样:
*** Settings ***
Library Renode
Library Collections
*** Variables ***
${DEVICE} /dev/ttyUSB0
${BAUD_RATE} 115200
*** Keywords ***
Open Serial Port
[Arguments] ${port} ${baud}
Log 打开串口 ${port},波特率 ${baud}
# 实际调用Renode关键字
*** Test Cases ***
Serial Port Open And Close
[Setup] Log 测试开始
Open Serial Port ${DEVICE} ${BAUD_RATE}
Close Serial Port
[Teardown] Log 测试结束
注意 [Setup] 和 [Teardown],这是 Robot Framework 的生命周期管理。每个用例执行前后都会自动调用,非常适合做资源初始化和清理。我建议你养成习惯——每个用例都写 Teardown,否则仿真器状态可能会污染下一个用例。
4.6 知识体系总览
下面这张图,是我自己梳理的 Robot Framework 在 Renode 测试中的知识结构。你可以把它当作本章的「地图」:
4.7 常用关键字速查表
下面这些关键字,是我在项目中用得最频繁的。建议你收藏一下:
| 关键字 | 作用 | 使用频率 |
|---|---|---|
Start Emulation |
启动 Renode 仿真 | 每个用例必用 |
Stop Emulation |
停止仿真,释放资源 | 每个用例必用 |
Write To Register |
写入寄存器 | 极高 |
Read From Register |
读取寄存器值 | 极高 |
Inject Fault |
注入故障(位翻转、毛刺等) | 高 |
Should Be Equal |
断言两个值相等 | 高 |
Sleep |
等待指定时间 | 中 |
Log |
输出日志 | 调试时常用 |
小技巧:在 Renode 的交互式控制台里,你可以用 help 命令查看所有可用关键字。我经常一边写用例一边开着控制台,随时查关键字签名,比翻文档快多了。
4.8 一个完整的实战用例
最后,我给你看一个我在项目中真实用过的用例。它测试的是「在时钟故障下,UART 能否正确报告错误状态」:
*** Settings ***
Library Renode
Library Collections
*** Variables ***
${UART_STATUS_REG} 0x40002008
${CLOCK_FAULT_BIT} 0x04
*** Keywords ***
UART Should Report Clock Fault
${status}= Read From Register ${UART_STATUS_REG}
${fault_flag}= Evaluate ${status} & ${CLOCK_FAULT_BIT}
Should Be True ${fault_flag} == ${CLOCK_FAULT_BIT}
Log UART正确报告了时钟故障
*** Test Cases ***
UART Clock Fault Detection
[Documentation] 验证UART在时钟故障时能设置状态寄存器的故障位
Start Emulation
# 注入时钟故障
Inject Clock Glitch target=uart frequency=500000
# 等待UART检测到故障
Sleep 100ms
# 检查状态寄存器
UART Should Report Clock Fault
# 清除故障
Write To Register ${UART_STATUS_REG} 0x00
Stop Emulation
这个用例虽然短,但包含了完整的测试流程:启动 → 注入故障 → 等待 → 断言 → 清理。你写测试用例时,也尽量保持这个结构。我在团队里定了一条规矩:没有 Teardown 的用例不准提交。虽然严格了点,但确实省了很多排查时间。