Bokeh 测试指南从本地单元测试到 CI 全矩阵验证的完整实践【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokehBokeh 是一个横跨 Python 与 TypeScript 的大型多语言项目其质量保障依赖一套分层、可组合的测试体系。本文以 Bokeh 官方开发者指南《Running tests》为骨架结合仓库内真实源码CI 工作流、pytest 配置、BokehJS 测试任务展开系统讲解本地如何按需运行代码库测试、Python 测试、BokehJS 测试与示例测试并深入剖析 GitHub Actions 上标准 CI 与 Full CI 的分工协作。读完本文你将掌握 Bokeh 贡献者筛选与运行测试的完整方法论以及各测试层级背后的实现原理。测试分层总览先判断该跑哪些测试Bokeh 规模庞大且跨语言依赖复杂而全面的测试与测试工具来保证一致性、防止回归。官方给出的核心原则是你不需要在本地搭建并运行全部测试——只要你向 Bokeh 的 GitHub 仓库发起 Pull RequestCI 就会在分支上跑完所有测试。本地只需要按改动范围精准打击。选择本地测试的通用决策矩阵如下你的改动范围建议运行的测试改动 Bokeh 代码库的任何内容代码库测试codebase tests编辑 Bokeh 的 Python 代码Python 单元测试unit tests工作涉及 UI 元素Python 单元测试 Python 集成测试改动与 BokehJS 相关的任何内容JavaScript 测试BokehJS tests总体而言只运行与你当前工作相关的特定测试是性价比最高的做法。Python 测试的筛选/排除方法见下文选择特定测试小节BokehJS 测试的筛选方法见BokehJS 测试小节。本地测试前置条件在运行任何 Bokeh 测试之前请确保已经完成 开发者环境配置 章节的全部步骤并检查以下三项依赖Sampledata示例数据确保 Bokeh 的 sampledata 已安装且保持最新pip install bokeh_sampledata如果你的系统无法安装 sampledata可以通过 pytest 的 marker 机制禁用相关测试见下文选择特定测试。Selenium 与 Web Driver部分测试依赖 Selenium 及对应的浏览器驱动。虽然也可以使用其他驱动但官方推荐 Selenium ChromeDriver Chrome 的组合。若系统中没有 Selenium同样可以禁用这些测试。文件描述符上限在部分 Unix 平台上服务端测试会打开大量文件建议将最大打开文件数提升到至少 1024ulimit -n 1024这些前置条件在仓库的 pytest 配置中也有对应体现tests/conftest.py 中注册了pytest_pluginsipython、managed_server_loop、networkx 等支持插件并定义了--driver、--bokeh-port、--no-js等命令行选项是测试体系能够灵活组合的基础。运行代码库测试最基础的一组测试是 Bokeh 的代码库测试codebase tests它涵盖使用Ruff检查 Python 代码使用ESLint检查 JavaScript 代码其他杂项检查如未使用的导入、多余空白等。任何对 Bokeh Python 或 JavaScript 代码库的编辑都应通过这组测试。在仓库顶层目录执行pytest tests/codebase从源码结构看这组测试由 tests/codebase 目录下的一系列测试文件组成包括test_ruff.pyRuff 静态检查、test_eslint.pyESLint 检查、test_isort.py导入排序、test_license.py许可头检查、test_vermin.pyPython 版本兼容性检查、test_no_client_server_common.py/test_no_tornado_common.py/test_no_request_host.py依赖边界与安全约束以及test_js_license_set.py等可见代码库测试实际是一组面向代码质量与工程规范的守护型检查。运行工具测试Bokeh 的开发者工具、CI 工具和发布工具都位于 tools 目录。对这些工具的改动应通过其专属测试套件pytest tests/tools仓库中 tests/tools 包含发布流程release、backporttests/tools/backport等工具链的测试用例确保工具本身的行为可验证、可回归。运行 Python 测试Bokeh 的 Python 侧测试基于pytest位于 tests 目录。每当修改 Python 代码时都应运行代码库测试与 Python 单元测试若改动涉及用户界面元素还应追加运行 Python 集成测试。常用 pytest 命令行参数以下参数在 Bokeh 的 pytest 测试中高频使用均在仓库的 pytest 配置中定义或由插件提供参数作用示例-k提供搜索字符串筛选特定测试pytest -k grid-m按 marker 选择/排除测试pytest -m not selenium tests/unit-n在多个 CPU 核心上并行分发测试auto表示使用全部核心pytest -n 4 tests/codebase依赖 pytest-xdist-v更详细的输出pytest -v tests/unit--driver为 Selenium 测试指定 Web 驱动chrome、firefox或safaripytest --driverfirefox tests/unit/--no-js跳过所有 JavaScript 代码只测 Pythonpytest --no-js tests/test_examples.py其中--driver与--no-js这两个选项在 tests/conftest.py 的pytest_addoption中显式注册--driver的合法取值为chrome/firefox/safari默认chrome--no-js为布尔开关用于只运行 Python 代码并跳过 JS。其余参数遵循 pytest 官方文档。Python 单元测试在仓库顶层执行以下命令运行 Python 单元测试pytest -m not selenium tests/unit这条命令会排除需要 Selenium 的单元测试。官方建议本地使用-m not selenium原因有二一是 Selenium 配置繁琐二是部分单元测试要求系统同时具备 geckodriver 与 ChromeDriver。在你创建 Pull Request 后CI 会运行全部测试包括基于 Selenium 的单元测试。如果你的系统已具备 Selenium geckodriver ChromeDriver也可以直接运行pytest tests/unitPython 单元测试代码覆盖率使用--covbokeh生成 Python 单元测试的覆盖率报告pytest --covbokehBokeh 的 Python 单元测试覆盖率应保持在90% 左右。注意覆盖率报告只对 Python 单元测试有意义其他 Python 测试和 BokehJS 的 JavaScript 代码均不生成覆盖率报告。也可以在运行特定子集时附带覆盖率例如生成 HTML 报告pytest --covbokeh --cov-reporthtml -m not selenium tests/unit/bokeh/test_objects.py覆盖率由 pytest 插件pytest-cov提供相关用法可参考 pytest-cov 官方文档。交叉集成测试Cross integration testsBokeh 有一类 Python 到 JS 的接口测试在 Bokeh 侧运行一个 Python 测试用例产生包含序列化文档的 JSON 输出该 JSON 被存放到仓库的tests/baselines/cross目录下仓库中可见 tests/baselines/cross 下按regressions/issue_*.json5形式组织的基线文件。新增测试用例时pytest tests/test_cross.py然后提交新的基线并重新运行测试——只有已提交的基线才会被测试运行器考虑。每个交叉测试用例都必须在 BokehJS 中有一个对应的集成测试位于bokehjs/test/integration/cross.ts。这些测试等价于典型的 BokehJS 集成测试对于不需要检查输出视觉形态的用例建议跳过图像 diff注意跳过图像采集并不会禁用*.blf基线文件的生成。交叉测试用例必须精心设计使 BokehJS 能产出一致、可重复的输出尤其是截图时。与其它测试一样允许使用随机数据——因为测试运行器会为 Python 与 numpy 的随机数生成器播种。请遵循 BokehJS 关于创建稳健集成测试的指南。运行全部可用测试在仓库顶层执行pytest即可运行全部可用测试Python 与 JavaScript 单元测试、示例测试与集成测试pytest并行运行单元测试使用-n参数将测试分发到多个 CPU 核心以加速pytest tests/unit -n auto # 所有物理 CPU 核心 pytest tests/unit -n logical # 所有逻辑 CPU 核心 pytest tests/unit -n 4 # 4 个 CPU 核心并行执行由pytest-xdist提供。选择特定测试向 pytest 传入路径即可测试 Bokeh 包的子集pytest tests/unit/bokeh/models/也可以传入具体文件运行单个测试pytest tests/unit/bokeh/models/test_grids.py另一种筛选方式是使用 marker。目前 Bokeh 的测试使用以下两个 markersampledata需要下载bokeh.sampledata的测试selenium需要 selenium 的测试。自定义 marker 的方法参见 pytest 官方文档Working with custom markers选择特定测试的更多选项参见Specifying which tests to run。提示关于新增与更新 Python 测试的方法参见 Bokeh 开发者指南中编写 Python 测试章节。运行 JavaScriptBokehJS测试BokehJS 的大部分 JavaScript 测试使用自研测试框架该框架要求系统安装 Google Chrome 或 Chromium需要较新版本。Bokeh 会优先在PATH中查找 Chrome/Chromium你也可以通过环境变量BOKEH_CHROME显式指定可执行文件路径。从源码看这一查找逻辑实现在 bokehjs/make/tasks/test.ts 的chrome()函数中若设置了BOKEH_CHROME则直接校验并使用它否则依次探测chromium_revision、chromium、chromium-browser、chrome、google-chrome等候选名并在 Linux/macOS/Windows 上补充各自平台的 Chrome 安装路径。同一文件中还定义了受支持的 Chromium 修订版supported_chromium_revision r3265对应 Chrome 141.0.7390.54与 CI 工作流中固定的CHROME_VER/CHROME_REV保持一致。运行全部 BokehJS 测试可以使用 pytest 一键运行 BokehJS 的全部测试pytest tests/test_bokehjs.py从源码看tests/test_bokehjs.py 本质上是在bokehjs子目录下以子进程方式执行node make test的包装器。你也可以直接在源码检出目录的bokehjs子目录下运行node make testnode make test会依次运行 codebase、defaults、unit 与 integration 四套测试见 bokehjs/make/tasks/test.ts 末尾的task(test, [test:codebase, test:defaults, test:lib])定义其中test:lib又组合了test:unit与test:integration。单独运行各测试套件在bokehjs子目录下使用node make test:suite_name可单独运行指定套件命令含义node make test:codebase代码库测试检查文件大小限制node make test:defaults默认值一致性测试检查 Bokeh Python 模型与 JavaScript 模型的默认值是否一致node make test:unitBokehJS 单元测试node make test:integration视觉集成测试将本地生成的图表与一组基线文件对比最后两个套件可以合并运行node make test:lib。用搜索字符串筛选 BokehJS 测试BokehJS 测试框架支持用-k参数传入搜索字符串区分大小写框架会尝试将搜索字符串与测试describe()/it()函数中定义的字符串匹配。例如$ node make test:integration -k Legend该命令只运行包含字符串 Legend 的集成测试。注意BokehJS 单元测试与集成测试需要较新版本的 Chrome 或 Chromium。测试框架会自动以能产出一致结果的正确设置启动浏览器。使用 devtools server 测试与审查除了命令行运行还可以使用 BokehJS devtools server——它同样要求系统安装 Chrome用于运行测试并审查视觉测试输出。首先在bokehjs子目录启动 devtools server$ node test/devtools server listening on 127.0.0.1:5777devtools server 支持两类操作审查视觉测试结果运行集成测试后在 Chrome 中打开显示的服务器地址通常为127.0.0.1:5777并追加/integration/report即可打开本地渲染图 vs 基线文件的对比视图逐项查看与基线存在差异的测试发起测试运行有两种方式从 JavaScript 控制台运行在浏览器中打开/unit、/defaults、/integration三个端点之一加载 BokehJS 与测试然后在 Chrome 的 JavaScript 控制台执行Tests.run_all()。这允许你在运行代码前设置断点也可以向函数的query参数传入搜索字符串、字符串列表或正则表达式来只运行特定测试例如Tests.run_all(query/[Ll]egend/);使用端点直接运行通过浏览器访问/unit/run、/defaults/run、/integration/run端点发起测试运行此外还支持两类 URL 过滤在 URL 后追加?ksome%20text按关键字过滤测试在 URL 后追加platformlinux、platformmacos或platformwindows只运行/查看特定平台的测试。注意大多数情况下本地以常规 Chrome GUI 运行的结果与 Bokeh CI 中使用 headless Chrome 的结果一致但极少数情况下 headless 与 GUI Chrome 会产生不同结果此时无法使用 GUI需要直接在 headless 浏览器中调试 BokehJS 代码参见开发者指南的 headless 调试章节。提示关于新增与更新 BokehJS 测试的方法参见 Bokeh 开发者指南中编写 BokehJS 测试章节。运行示例测试除了 Python 与 JavaScript 聚焦的测试Bokeh 还维护一套示例测试examples tests运行仓库中精选的一批示例检查每个示例能否无错误地构建同时生成带截图的可视化报告。示例测试使用一套自定义配置的 Chrome 测试框架因此官方不建议在本地运行——创建 Pull Request 后 CI 会运行全部示例测试。如需本地运行需先在后台启动定制版 headless Chrome必须从bokehjs目录启动。在源码检出目录顶层依次执行cd bokehjs node make test:run:headless这会启动一个 headless Chrome 工具。然后另开一个终端在源码检出目录顶层运行pytest tests/test_examples.py从 bokehjs/make/tasks/test.ts 源码可见test:run:headless会以--headlessnew、固定远程调试端口默认 9222以及一系列保证渲染一致性的参数如--force-color-profilesrgb、--force-device-scale-factor1、--font-render-hintingnone启动 Chromium并通过keep_alive()保持后台运行。运行测试时pytest 会生成包含每个示例视觉输出截图的报告examples-report.html位于你运行测试的同一目录注意示例测试不会分析生成的截图因此不会因为视觉输出而失败——你需要人工检查测试报告。此外示例测试还会在同一目录生成日志文件examples.log。Continuous IntegrationCI每次在 Bokeh 的 GitHub 仓库创建 Pull Request或向已有 PR 分支推送新提交时Bokeh 的 GitHub Actions CI 都会在分支上运行全部可用测试。所有当前与历史 CI 运行记录可在仓库的 Actions 页面查看。环境文件Bokeh 的 CI 在 Linux、macOS 与 Windows 上运行测试并覆盖多个 Python 版本。各种测试环境由其对应的 YAML 文件定义位于 conda 目录——仓库中可见environment-test-3.12.yml、environment-test-3.13.yml、environment-test-3.14.yml、environment-test-3.14t.ymlfree-threaded、environment-test-core.yml、environment-test-minimal-deps.yml、environment-test-downstream.yml等。如果你新增或变更了依赖需要同步更新这些文件。CI 工作流Bokeh 使用多个 CI 工作流在速度与覆盖率之间取得平衡标准 CIbokeh-ci.yml每次 push 与 pull request 都会运行提供快速反馈测试关键跨平台功能同时控制 CI 耗时。覆盖约 11 个 job单元测试最新 Python3.14在所有平台Ubuntu、macOS、Windows上运行代码库检查所有平台使用 Python 3.12其他测试仅 Linux示例、minimal-deps、core-deps、文档特性启用 cancel-in-progress加快迭代速度对照仓库中的 .github/workflows/bokeh-ci.yml可以看到其 job 编排build构建 Bokeh 与 BokehJS→codebase三平台跑pytest tests/codebase与tests/tools→typingmypy、pyright 类型检查3.12/3.14→examples仅 Ubuntu安装 Chromium 后node make test:spawn:headless再跑tests/test_examples.py并上传examples-report产物→unit-test三平台 Python 3.14安装 Playwright Chromium 后跑单元测试→minimal-deps/core-deps仅 Linux 的依赖矩阵测试→documentation构建文档。完整 CIbokeh-ci-full.yml每天 UTC 2:00 自动运行维护者也可手动触发测试所有平台 × Python 版本的组合实现完整覆盖。覆盖 29 个 job平台Ubuntu 24.04、macOS latest、Windows latestPython 版本3.12、3.13、3.14测试套件所有测试类型在所有组合上运行仓库中的 .github/workflows/bokeh-ci-full.yml 还额外包含两个值得关注的 jobunit-test-free-threaded在 free-threaded Python 3.14t 上验证 GIL 禁用PYTHON_GIL0场景下 Bokeh 的单元测试与并发压力测试-m free_threadingdeployment-proxy与server-e2e覆盖 ASGI/Tornado 前端经 nginx/apache 反向代理的部署场景以及 uvicorn/hypercorn 等 ASGI 服务器的端到端示例测试另有notify-nightly-failure与notify-pr在 nightly 失败时自动向 GitHub Discussions 发通知手动触发时在关联 PR 上评论完整结果。手动触发完整工作流通过 GitHub Web UI进入仓库的 Actions 标签页在工作流列表中选择 Bokeh-CI-Full点击右上角 Run workflow选择要测试的分支可选为手动运行填写原因点击 Run workflow 开始通过 GitHub CLI# 在指定分支上触发 gh workflow run bokeh-ci-full.yml --ref branch-name # 在某个 PR 的分支上触发 gh workflow run bokeh-ci-full.yml --ref $(gh pr view PR_NUMBER --json headRefName --jq .headRefName) # 附带自定义原因 gh workflow run bokeh-ci-full.yml --ref branch-name -f reasonTesting before release手动触发在重大发布前、依赖更新后或调查平台特定 bug 时尤其有用。CI 礼仪CI 服务为开源项目提供有限的免费构建资源。请在推送到 GitHub 前将提交分组为有意义的变更块而不是逐个提交推送以便为其他需要使用这些有限资源的开发者着想。结语Bokeh 的测试体系可以概括为三条主线代码库测试守质量底线Ruff/ESLint 与工程规范检查、Python 与 BokehJS 分层测试保功能正确单元、集成、默认值一致性、视觉基线对比、CI 双工作流保发布信心标准 CI 快速反馈 Full CI 全矩阵覆盖。对贡献者而言本地按改动范围精准运行对应测试、把全量验证交给 CI是最高效的协作方式而对希望深入了解其工程实践的读者tests、tests/codebase、.github/workflows/bokeh-ci.yml、.github/workflows/bokeh-ci-full.yml 与 bokehjs/make/tasks/test.ts 是继续研读的最佳入口。【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考