新闻详情

新闻详情

首页 / 资讯中心 / 详情

marimo 笔记本测试实战:pytest 单元测试、doctest 文档测试与测试执行机制解析

发布时间:2026/9/13 9:41:59来源:尧图网络
marimo 笔记本测试实战:pytest 单元测试、doctest 文档测试与测试执行机制解析
marimo 笔记本测试实战:pytest 单元测试、doctest 文档测试与测试执行机制解析【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo本文基于 marimo 仓库中的examples/testing测试示例目录,系统讲解两种在 marimo 笔记本中编写与运行测试的完整方案:用pytest对笔记本做单元测试,以及用 Python 标准库doctest对 docstring 代码片段做文档测试。读完本文,你将掌握测试笔记本的两种写法、测试单元(cell)的识别规则、pytest fixture 在 marimo 中的使用边界,以及 marimo 编辑器内响应式自动跑测试这一行为的源码级实现机制。为什么 marimo 笔记本可以被常规工具测试marimo 笔记本与普通 Python 脚本的本质区别只在于它使用了marimo.App()与app.cell/app.function装饰器来组织代码——它本身仍然是一个纯 Python 文件。这带来一个直接结论:所有面向 Python 的测试工具(pytest、doctest、coverage、lint 工具)都可以原样作用于笔记本文件,不需要额外的测试框架转换层。examples/testing目录下的 README(examples/testing/README.md)正是围绕这一点给出两个基础示例:方案示例文件运行方式用 pytest 测试笔记本test_with_pytest.pypytest test_with_pytest.py用 doctest 测试 docstring 代码片段running_doctests.py作为笔记本直接运行官方文档 docs/guides/testing/index.md 对这两类测试的总结是:Because marimo notebooks are stored as Python, test them like any other Python program.(因为 marimo 笔记本以 Python 形式存储,像测试任何 Python 程序一样测试它们即可)。方案一:用 pytest 测试笔记本最小可运行示例完整阅读 examples/testing/test_with_pytest.py,其全部内容如下:import marimo __generated_with 0.23.9 app marimo.App() app.function def inc(x): return x 1 app.cell def test_answer(): assert inc(3) 5, This test fails return app.cell def test_sanity(): assert inc(3) 4, This test passes return if __name__ __main__: app.run()这个文件演示了三个关键写法:被测对象放在app.function中。inc是一个由 marimo 管理的纯函数,测试 cell 可以直接引用它,无需关心 import 与依赖;测试 cell 以test_前缀命名 cell 函数。cell 名test_answer与test_sanity使这两个 cell 被识别为测试单元;一个 cell 里写一个断言。失败与通过的测试各占一个 cell,便于定位。在命令行执行:pytest test_with_pytest.py预期结果:test_sanity通过,test_answer失败(断言inc(3) 5不成立),pytest 会给出标准的失败报告。这正是 docs/guides/testing/pytest.md 中Testing at the command-line一节的用法——对任何笔记本文件直接pytest my_notebook.py,pytest 会自动发现其中所有名字以test_开头的 cell,或仅包含test_函数与Test类的 cell。测试 cell 的识别规则marimo 对哪些 cell 是测试 cell有明确而保守的判定,来自官方 pytest 指南:名字以test_开头的cell 函数被视为测试;cell整体只包含测试代码(名字以test_开头的函数、名字以Test开头的类、或带pytest.fixture装饰的函数)时,该 cell 会被测试运行器执行;如果 cell 里混入了其他内容(辅助函数、常量、变量、import 等),该 cell 会被跳过——官方建议把辅助函数移到单独的 cell。test_with_pytest.py中的两个测试 cell 恰好都只含assert语句,因此会被完整执行;而app.function def inc(x)所在 cell 不是测试,它只负责提供被测函数。编辑器内的响应式测试(reactive tests)除了命令行,marimo 在编辑模式下还会自动发现并执行笔记本内的测试。这一行为的开关是配置项runtime.reactive_tests,其默认值为True——见 marimo/_config/config.py 中 runtime 默认配置块:reactive_tests: True,该配置项的作用说明(同文件 docstring):reactive_tests: ifTrue, marimo will automatically run pytest on cells containing only test functions and test classes.如果不需要编辑器自动跑测试,可在配置文件中关闭runtime.reactive_tests。从源码结构看,这条响应式测试链路的挂载点在 marimo/_runtime/kernel_lifecycle.py 的_build_hooks中:if is_edit_mode and user_config[runtime].get(reactive_tests, False): hooks.add_post_execution(attempt_pytest, Priority.LATE)也就是说,只有编辑模式(is_edit_mode)下才会注册attempt_pytest这个cell 执行后钩子(定义于 marimo/_runtime/runner/hooks_post_execution.py);marimo run(非编辑模式)不会触发自动测试。当某个测试 cell 重新执行后,marimo 会调用 pytest 对该 cell 内容做一轮测试,并把结果呈现回编辑器界面。底层实现:marimo 如何把 cell 骗过 pytestpytest 通过静态扫描模块来收集测试,它看到的笔记本文件里只有app.cell装饰的函数,而不是真正的可收集测试对象。marimo 在 marimo/_ast/pytest.py 中用 AST 改写的方式解决了这个问题,核心机制包括:1. 顶层test_cell 的签名重写(wrap_fn_for_pytest)当 cell 本身以test_开头时,marimo 解析 cell 函数的源码 AST,生成一个桩函数(scaffold):# 生成结果的示意形态 def name_of_fn_passed(vars_ending_in_fixture, ...) - Any: return cell(vars_ending_in_fixturevars_ending_in_fixture)这个桩函数把 cell 的参数中以_fixture结尾的名字视为 pytest fixture 注入点(见 marimo/_ast/pytest.py 的wrap_fn_for_pytest:fixtures [arg for arg in args if arg.endswith(_fixture)])。这样 pytest 在收集时看到的是一个签名为(fixture_name,)的普通测试函数,调用它时才会真正执行对应 cell 的__call__。2. 多测试 cell 的类封装(build_test_class)如果一个测试 cell 内部定义了多个测试函数或测试类,marimo 会在模块层动态构造一个名为MarimoTestBlock_n的类(常量MARIMO_TEST_STUB_NAME MarimoTestBlock,见 marimo/_ast/pytest.py),把 cell 内的每个test_函数包装为staticmethod、每个Test*类封装为嵌套测试类,并注入到笔记本模块的命名空间中供 pytest 收集。官方文档示例的输出中就出现了这种形态:test_notebook.py::MarimoTestBlock_0::test_parameterized[3-4] PASSED test_notebook.py::MarimoTestBlock_0::test_parameterized[4-5] PASSEDbuild_test_class的 docstring 特别说明了一个鲁棒性设计:每个测试都是符号化解析的,某个测试定义即使写错了,也不会破坏整个测试套件的定义或阻止套件运行。3. fixture 的解析方式marimo 支持pytest.fixture与pytest.mark.parametrize等装饰器(is_pytest_decorator、has_fixture_decorator等函数通过 AST 识别pytest.*装饰器)。但存在一条重要限制(来自官方 pytest 指南的 Fixture Limitations):在一个 cell 中定义的 fixture 不能被另一个 cell 中的测试使用。原因是 pytest 收集测试是静态的:它只解析笔记本文件、不执行。因此 pytest 只能看到模块级 fixture(来自conftest.py或 import 的模块)以及与被测测试同作用域定义的 fixture。marimo 的 cell 执行顺序由运行时依赖图决定,静态分析无法得知哪些 fixture 会可用;而且仅为发现 fixture 就完整运行一遍笔记本代价过高。官方推荐的两种写法:在笔记本的setup cell(with app.setup:)中 import fixture,使其进入模块级作用域;或者把 fixture 与被测测试放在同一个 cell中(如 class-scope fixture 与test_方法定义在同一 cell 内);最通用的做法仍是使用标准的conftest.py。如果 fixture 装饰器在求值时失败,marimo 会让对应测试抛出ValueError,并提示 Consider exposing relevant variables in app.setup(见 marimo/_ast/pytest.py 的_make_fails)。命令行 pytest 与响应式测试的分工总结维度命令行pytest my_notebook.py编辑器响应式测试触发方式手动执行 pytestcell 执行后自动触发(编辑模式 reactive_testsTrue)覆盖范围全部test_cell 与纯测试 cell最近变更/执行的测试 cell适用场景CI、完整回归开发时即时反馈方案二:用 doctest 测试 docstring 代码片段对于带文档字符串的函数,Python 标准库 doctest 指向的完整示例是 examples/testing/running_doctests.py:import marimo __generated_with 0.19.7 app marimo.App(widthmedium) app.cell def _(): import marimo as mo return (mo,) app.function def euclid_mcd(a: int, b: int) - int: Return the MCD between positive a, b. euclid_mcd(42, 24) 6 euclid_mcd(24, 42) 6 euclid_mcd(42, 42) 42 assert a 0 assert b 0 if a b: a, b b, a if (a ! b): r a - b return euclid_mcd(b, r) return a app.cell def _(mo): # Include a reference to each function to test euclid_mcd import doctest failures, success doctest.testmod(verboseTrue) mo.md(fSuccess: {success}, Failures: {failures}) return if __name__ __main__: app.run()这个示例有三个值得注意的技术点:被测函数放在app.function中,docstring 内写交互示例。euclid_mcd(欧几里得求最大公约数)的 docstring 包含 3 组输入/输出对,覆盖非对称参数(42, 24)与(24, 42)以及相等情况(42, 42);运行 doctest 的 cell 必须显式引用被测函数。注意# Include a reference to each function to test注释下单独一行写着euclid_mcd——marimo 的 cell 依赖分析基于源码中出现的名字,若 cell 不引用euclid_mcd,该 cell 与函数之间就没有依赖边,doctest 运行时可能拿不到这个函数。把对函数的显式引用作为依赖锚点,是 marimo 笔记本中使用跨 cell 定义对象的通用技巧;测试结果回显为 Markdown。doctest.testmod(verboseTrue)返回(failures, success)计数,示例通过mo.md(...)把Success: N, Failures: M渲染到笔记本界面,失败的片段会以**test**形式逐条打印在标准错误流中。由于笔记本就是 Python 程序,你也可以在外部直接对该文件跑python -m doctest风格的检查,或者把doctest.testmod放进一个test_cell 里交给 pytest 收集——两种方式都成立。小结与延伸阅读marimo 的测试策略可以概括为一句话:不造新框架,复用 Python 生态既有工具。单元/集成测试:给 cell 命名test_*,用 pytest 从命令行或编辑器响应式钩子执行;底层由 marimo/_ast/pytest.py 的 AST 桩函数与MarimoTestBlock类封装完成对 pytest 收集器的适配;文档测试:用doctest.testmod直接验证app.function的 docstring 示例;开关与默认值:runtime.reactive_tests默认True,可在配置中关闭编辑器的自动测试行为;注意事项:纯测试 cell 与混合 cell 的识别差异、跨 cell fixture 不可见的静态收集限制。相关仓库路径索引:路径内容examples/testing/README.md本文主参考:两个测试示例的说明examples/testing/test_with_pytest.pypytest 测试笔记本示例examples/testing/running_doctests.pydoctest 文档测试示例docs/guides/testing/pytest.mdpytest 指南:识别规则、fixture 限制、完整输出示例docs/guides/testing/doctest.mddoctest 指南marimo/_ast/pytest.py测试 cell → pytest 收集对象的 AST 转换实现marimo/_runtime/kernel_lifecycle.py响应式测试钩子attempt_pytest的注册点marimo/_config/config.pyreactive_tests默认值【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

用 R 与 ggplot2 可视化数据分布:直方图与密度图实战(Data-Science-For-Beginners 第 10 课) 2026/9/13 10:27:03

用 R 与 ggplot2 可视化数据分布:直方图与密度图实战(Data-Science-For-Beginners 第 10 课)

用 R 与 ggplot2 可视化数据分布:直方图与密度图实战(Data-Science-For-Beginners 第 10 课) 【免费下载链接】Data-Science-For-Beginners 10 Weeks, 20 Lessons, Data Science for All! 项目地址: https://gitcode.com/GitHub_Trending/d…

阅读更多 →
superpowers:给AI编程代理装技能的实战指南 2026/9/13 10:27:03

superpowers:给AI编程代理装技能的实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
GSYVideoPlayer RTMP 模块 16 KB 页面大小兼容构建与验证指南 2026/9/13 10:27:03

GSYVideoPlayer RTMP 模块 16 KB 页面大小兼容构建与验证指南

GSYVideoPlayer RTMP 模块 16 KB 页面大小兼容构建与验证指南 【免费下载链接】GSYVideoPlayer Video players (IJKplayer, ExoPlayer, MediaPlayer), HTTPS, 16k page size, danmaku (bullet chat) support, external subtitles, support for filters, watermarks, and GIF sc…

阅读更多 →
PDFPatcher书签批量编辑实操:导出XML再回补,一份长PDF的目录十分钟整理好 2026/9/13 10:27:03

PDFPatcher书签批量编辑实操:导出XML再回补,一份长PDF的目录十分钟整理好

PDFPatcher书签批量编辑实操:导出XML再回补,一份长PDF的目录十分钟整理好 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱,可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档,探查文档结构,提取图片、转成图…

阅读更多 →
环形Halbach磁体阵列原理与工程实现指南 2026/9/13 10:27:03

环形Halbach磁体阵列原理与工程实现指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
OpenHarmony中高效处理CSV数据的实践与优化 2026/9/13 10:24:03

OpenHarmony中高效处理CSV数据的实践与优化

1. 项目背景与核心价值在OpenHarmony生态中构建数据报表功能时,CSV格式凭借其独特的优势成为跨平台数据交换的首选方案。我在实际项目中验证过,一个10万行的数据表用CSV导出仅需不到200ms,而同等数据量使用Excel格式则需要3秒以上。这种性能差…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞