news 2026/9/20 14:56:44

MicroPython 测试编写指南:从 tests 目录到 run-tests.py 测试运行器全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MicroPython 测试编写指南:从 tests 目录到 run-tests.py 测试运行器全解析

MicroPython 测试编写指南:从 tests 目录到 run-tests.py 测试运行器全解析

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

MicroPython 拥有一个独立于各移植(port)的跨平台测试体系,集中存放于仓库的tests/目录,并以run-tests.py作为统一测试运行器。本篇指南以官方开发文档 docs/develop/writingtests.rst 为骨架,结合 tests/run-tests.py 与 tests/test_utils.py 的源码实现,系统讲解测试的组织结构、编写规范、run-tests.py全部命令行参数与典型使用场景。读完本文,你将能够为 MicroPython 新增高质量测试,并在 Unix 移植、开发板串口、webassembly 等多种目标实例上运行、筛选、并行化执行测试,以及利用失败结果文件快速定位回归问题。

测试体系总览:目录结构与运行方式

MicroPython 的测试全部位于仓库根的tests/目录。该目录下按功能域划分了多个子目录,每个子目录中存放若干.py测试脚本及对应的.exp期望输出文件(*.exp存在与否决定了测试结果的判定方式,详见后文)。典型的顶层结构如下:

. ├── basics # 语言基础特性测试 ├── extmod # extmod 模块测试 ├── float # 浮点运算测试 ├── micropython # MicroPython 特有功能测试 ├── run-tests.py # 测试运行器(Python 脚本) ...

除上述目录外,仓库中还存在大量其他分类,从 run-tests.py 的默认测试目录集合可以看到:basicsmicropythonmiscextmodstress是通用默认目录;若目标支持内联汇编、线程、浮点、Unicode,会追加inlineasm/<arch>threadfloatunicode;同时会加载平台相关的ports/<port>目录(如ports/unixports/stm32);PC 平台(unix/windows)还会额外运行importcmdlineio测试。

新增测试的方式非常简单:在某个现有子目录(或新建子目录)中创建一个.py文件即可。对于自定义移植(custom port),官方建议将专属测试放在tests/之外独立维护,这样既不会污染上游测试集,也便于随移植单独分发。

第一个测试:print 驱动的结果对比模型

MicroPython 测试的核心判定模型是:将测试脚本在 MicroPython 上的输出,与同一脚本在 CPython 上的输出进行逐字节对比。因此,编写测试的第一原则是——用print语句显式输出测试结果。没有输出(或输出不稳定)的测试无法参与对比。

tests/ports/unix/子目录下新建print.py,内容如下:

def print_one(): print(1) print_one()

随后进入 unix 移植目录执行make tests,该目标会先构建 MicroPython 可执行文件,再调用 run-tests.py(见 ports/unix/Makefile),新的测试便会出现在输出中:

$ cd ports/unix $ make tests skip unix/extra_coverage.py pass unix/ffi_callback.py pass unix/ffi_float.py pass unix/ffi_float2.py pass unix/print.py pass unix/time.py pass unix/time2.py

每一行以pass/fail/skip等状态开头,随后是测试相对tests/的路径。除了make tests,unix 移植的 Makefile 还提供了若干便捷目标(见 ports/unix/Makefile):

  • make test-failures:等价于./run-tests.py --run-failures,只重跑上次失败的测试;
  • make test/<name>:等价于-d <name>,只运行指定目录;
  • make test_full_no_native/make test_full:以--via-mpy--emit native等组合执行更完整的回归。

编写测试的规范与技巧

用 print 表达断言

因为测试通过比对输出来判定,所以断言本质上就是“把结果打出来”。任何期望验证的值、异常行为、边界情况,都应转化为确定的打印内容。若测试涉及名称或字符串相关功能,官方建议同时覆盖英文/ASCII 与非英文/非 ASCII 文本,并附带 Unicode 示例;同时务必用英文注释说明 Unicode 文本的含义与意图,从而确保 Unicode 支持在跨平台环境中被充分验证。

.py.exp 文件:脱离 CPython 的期望输出

并非所有测试都能与 CPython 对比——MicroPython 特有功能(如micropython模块、内存分配失败路径、extra_coverage等)在 CPython 中根本不存在。此时可为测试提供同名的.py.exp文件,作为对比基准(truth)。run-tests.py 的判定优先级在源码中有明确说明(见 tests/run-tests.py):优先使用<test>.exp文件作为期望输出,否则才回退到用 CPython 运行测试生成期望输出;使用 native emitter 时则优先查找<test>.native.exp

特殊头部指令与 feature_check

部分测试文件支持#开头的特殊指令。例如cmdline/目录下的测试可通过# cmdline: <args>头指定传给 unix 可执行文件的命令行参数(见 tests/run-tests.py),# sigint:头用于指示需要发送 Ctrl-C 信号。另外,tests/feature_check 目录存放的是“能力探测脚本”(如target_info.pyfloat.pyinlineasm.py),run-tests.py会先在目标实例上执行这些探测脚本,自动获取平台、架构、浮点精度、线程与 Unicode 支持等信息(见 tests/run-tests.py),据此决定默认运行哪些测试目录。这也是为什么同一套测试在 PC 与开发板上会有不同取舍。

run-tests.py 命令行参数详解

目标实例与设备选择(-t)

不传-t时默认使用 unix 移植。-t/--test-instance支持的取值类型在 tests/test_utils.py 中有权威定义:

取值含义
unix使用 unix 移植,由环境变量MICROPY_MICROPYTHON指定可执行文件(默认是 unix 或 windows 移植的标准变体,视宿主平台而定)
webassembly使用 webassembly 移植,由MICROPY_MICROPYTHON_MJS指定(默认ports/webassembly/build-standard/micropython.mjs
port:<device>连接并使用给定的串口设备
a<n>连接/dev/ttyACM<n>
u<n>连接/dev/ttyUSB<n>
c<n>连接COM<n>(Windows)
exec:<command>执行命令并挂接到其 stdin/stdout
execpty:<command>执行命令并挂接到其打印出的/dev/pts/<n>设备
<a>.<b>.<c>.<d>连接给定的 IPv4 地址
其他任意值视为串口设备路径

设备快捷方式到真实路径的转换逻辑见 tests/test_utils.py。连接串口实例时,还可配合-b/--baudrate(默认 115200)、-u/--user(默认micro)、-p/--password(默认python)设置波特率与 telnet 登录凭据。硬件目标通过 raw REPL 执行测试,单测超时默认 30 秒(MICROPY_TEST_TIMEOUT),连续 3 次 raw REPL 失败即中止整轮测试(见 tests/test_utils.py)。

测试选择(-d / -i / -e / 文件参数)

  • -d, --test-dirs:指定一个或多个测试目录,如-d basics
  • -i, --include REGEX:仅运行路径/文件名匹配该正则的测试;
  • -e, --exclude REGEX:排除路径/文件名匹配该正则的测试;
  • 位置参数files:直接列出具体测试文件,如float/builtin*.py

-i/-e可多次出现,按命令行顺序依次生效,采用正则 search(而非 match)语义,最后一个匹配到的规则决定取舍(见 tests/run-tests.py):

./run-tests.py -i async # 先全部排除,再包含含 "async" 的测试 ./run-tests.py -e '/big.+int' # 先全部包含,再按正则排除 ./run-tests.py -e async -i async_foo # 排除 async,但仍包含 async_foo

执行选项(--emit / --via-mpy / --heapsize / -j)

  • --emit <EMITTER>:指定 MicroPython 发射器,取值为bytecode(默认)或native
  • --via-mpy:先将.py编译为.mpy再执行,模拟跨编译分发场景。实现上通过mpy-cross将脚本编译进内存,再利用注入的导入钩子加载(见 tests/test_utils.py 与 tests/test_utils.py)。可通过环境变量MICROPY_MPYCROSS指定特定版本的mpy-cross,并用--mpy-cross-flags透传额外参数(例如按检测到的架构自动追加-march=...,见 tests/run-tests.py);
  • --heapsize:为测试设置堆大小(如-X heapsize=64k之类,webassembly 实例上会透传该参数);
  • -j, --jobs N:并行运行的测试数,默认取 CPU 核数;
  • 其他实用选项:--dry-run仅列出将运行的测试;--keep-path不清空MICROPYPATH(默认会清空搜索路径,确保只使用内置模块、extmod 与 unittest 标准库,见 tests/run-tests.py);-c/--trace-output实时打印测试输出;--begin PROLOGUE在执行测试前先运行一段前导脚本。

结果管理(-r / --print-failures / --clean-failures / --run-failures)

  • -r, --result-dir:结果目录,默认tests/results/
  • --print-failures:打印失败测试的期望(.exp)与实际(.out)差异并退出。注意run-tests.py在运行前就会处理该选项(见 tests/run-tests.py),对已有结果文件做差异展示;
  • --clean-failures:删除上次失败遗留的.exp.out文件及结果记录;
  • --run-failures:仅重跑上次失败的测试,从results/_results.json中读取失败列表(见 tests/run-tests.py),且不能与files/-d同时使用。

测试失败时,运行器会在结果目录生成一对<test>.out(MicroPython 实际输出)与<test>.exp(期望输出);整轮结束后还会把参数与全部结果(pass/fail/skip/ignored)序列化到results/_results.json(见 tests/test_utils.py)。

常用组合示例

# 仅用 native 发射器运行 basics 与 extmod ./run-tests.py --emit native -d basics extmod # 排除 async 相关测试 ./run-tests.py -e async # 只运行匹配 *_pep_* 的测试 ./run-tests.py -i *_pep_* # 并行运行一组特定文件 ./run-tests.py -j 4 basics/list*.py # 在连接的 ESP32 开发板上运行 ./run-tests.py -t /dev/ttyUSB0 # 等价写法 ./run-tests.py -t u0 # 只重跑上次失败的测试 ./run-tests.py --run-failures

测试运行的其他入口与环境变量

从 tests 目录直接运行

tests/目录下直接执行./run-tests.py(不传任何参数)即可按默认规则自动探测目标并运行内置测试集:

$ cd tests $ ./run-tests.py # 默认 unix 移植 $ ./run-tests.py -t /dev/ttyACM0 # 运行在开发板上 $ ./run-tests.py -d basics # 只运行一个目录 $ ./run-tests.py float/builtin*.py # 运行指定文件

关键环境变量

环境变量作用默认值
MICROPY_MICROPYTHONunix/windows 移植的可执行文件路径ports/unix/build-standard/micropython(或 windows 版)
MICROPY_MICROPYTHON_MJSwebassembly 移植的.mjs路径ports/webassembly/build-standard/micropython.mjs
MICROPY_CPYTHON3用于生成期望输出的 CPython 解释器python3(Windows 下为python
MICROPY_MPYCROSS--via-mpy使用的mpy-cross路径mpy-cross/build/mpy-cross
MICROPY_DIFF失败差异展示所用的 diff 命令diff -u
MICROPY_TEST_TIMEOUT单个测试的超时秒数30

上述默认值均可在 tests/test_utils.py 与 tests/run-tests.py 中找到实现依据。需要说明的是,CPython 端以-BS参数启动(不生成.pyc、只访问核心标准库),并强制PYTHONIOENCODING=utf-8,以保证跨平台输出一致性。

质量保障机制:跳过、已知脆弱测试与报告

为兼顾不同目标的能力差异,run-tests.py内置了多级“跳过/豁免”机制(见 tests/run-tests.py):

  • --via-mpy专用跳过表:某些打印文件名、依赖源码路径的测试在.mpy模式下不适用;
  • emitter 专用跳过表:native 发射器暂不支持raise varargsraise fromsys.settrace等特性的测试会被跳过;
  • 平台专用跳过表:按esp8266minimalnrfrp2webassemblyzephyr等平台列出已知不适用或超时的测试,例如 esp8266 上stress/list_sort.py会触发看门狗;
  • 错误报告级别跳过表MICROPY_ERROR_REPORTINGterse/none时跳过依赖详细异常输出的测试;
  • 已知脆弱测试(flaky):即使失败也会被重新归类为ignored而不影响 CI 退出码,例如thread/thread_gc1.py(GC 竞态)、cmdline/repl_lock.py(REPL 时序)等,规则以(原因, 平台)元组维护;
  • target_wiring 注入:少数依赖硬件时序的测试(如extmod/machine_uart_tx.pyextmod_hardware/machine_pwm.py)需要import target_wiring,运行器会自动按板级/移植级匹配tests/target_wiring/中的脚本注入(见 tests/run-tests.py)。

这些机制共同保证了“同一份测试集,跨 PC、MCU 与 wasm 三种形态都能给出稳定且可信的结果”。运行结束后,摘要会依次输出执行数、通过数、已知脆弱忽略数、跳过数(含“因太大而跳过”)、失败数,并全部落盘到results/_results.json供 CI 消费(见 tests/test_utils.py)。

结语:写测试的正确姿势

回顾整个体系,MicroPython 的测试哲学可以概括为三点:输出即断言(一切靠 print 与期望文件对比)、差异即回归(CPython 与 MicroPython 的双引擎对照,辅以.py.exp承载 MicroPython 特有语义)、平台即参数(通过-t、feature_check 探测与多级跳过表,让一套测试适配从桌面到 MCU 的全部运行环境)。掌握 docs/develop/writingtests.rst 中描述的目录组织、文件命名与run-tests.py参数,再结合 tests/run-tests.py 与 tests/test_utils.py 的源码细节,你就能为任何 MicroPython 移植写出既规范又便于长期维护的测试。

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 14:56:18

读懂华为169页ISC战略方案:智慧供应链规划的核心逻辑

简介&#xff1a;华为智慧供应链ISC战略规划项目方案&#xff08;169页&#xff09;是一份聚焦企业数字化转型与供应链升维的战略级PPT&#xff0c;适合供应链管理者、数字化转型顾问及企业中高层学习参考。内容系统梳理客户体验与服务趋势变化&#xff0c;结合B2B CRM案例&…

作者头像 李华
网站建设 2026/9/20 14:56:03

51单片机驱动BMP280气压计:I2C读写到海拔高度计算全解析

简介&#xff1a;面向51单片机学习者和嵌入式初学者的BMP280气压计完整实现资料&#xff0c;围绕气压、温度与海拔高度的数据解析展开&#xff0c;解决从底层驱动到上位显示的核心问题。工程适配0.96英寸OLED与LCD1602双显示方案&#xff0c;同时支持串口上传数据&#xff0c;便…

作者头像 李华
网站建设 2026/9/20 14:55:32

研华PCI-1680U驱动安装与CAN调试链路实战

简介&#xff1a;CAN总线作为工业控制领域应用最广泛的现场总线之一&#xff0c;以其高可靠性和实时性支撑着设备间的数据交换。其底层通信依赖CAN控制器对帧格式、验收滤波和错误处理的管理&#xff0c;而驱动层则是连接操作系统与硬件控制器的关键桥梁。在工控场景中&#xf…

作者头像 李华
网站建设 2026/9/20 14:55:25

SolidWorks 2024许可错误(-8,544,0)排查与解决方案

## 1. 问题现象与背景分析最近在部署SolidWorks 2024时&#xff0c;不少工程师遇到了一个典型的许可报错&#xff1a;"无法获得下列许可 SOLIDWORKS Standard。无效的(不一致的)使用许可号码。(-8,544,0)"。这个错误代码看似简单&#xff0c;实则涉及许可证验证机制的…

作者头像 李华
网站建设 2026/9/20 14:54:47

需求追溯驱动的车载ECU全生命周期验证体系

简介&#xff1a;面向汽车电子领域ECU开发、系统设计与测试验证工程师的专题资料&#xff0c;系统梳理从用户外部需求到ECU软件落地的全流程&#xff0c;核心逻辑包括外部需求向系统需求转化、系统需求“双轨并行”推进、软件需求向组件需求层层拆解&#xff0c;以及组件级、软…

作者头像 李华