ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Metabase 端到端测试实践指南:基于 Cypress 的 E2E 测试体系全解析

Metabase 端到端测试实践指南:基于 Cypress 的 E2E 测试体系全解析 Metabase 端到端测试实践指南基于 Cypress 的 E2E 测试体系全解析【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabaseMetabase 使用 Cypress 构建了一套完整的端到端E2E测试体系用于对整个应用——包括前端、后端和应用数据库——进行整体验证。本文基于 Metabase 官方开发者指南docs/developers-guide/e2e-tests.md结合仓库中的真实源码e2e/目录下的 runner、support、scenarios 等实现系统讲解 E2E 测试的启动方式、测试结构、辅助机制、快照体系、Snowplow/SMTP/翻译字典等特殊场景以及 CI 与调试技巧帮助你快速上手并为 Metabase 编写高质量的端到端测试。什么是 Metabase 的 E2E 测试端到端测试是运行在真实 Web 浏览器中的 JavaScript 脚本访问不同的 URL、点击各种 UI 元素、输入文本并断言预期行为是否发生例如界面上出现某个元素或发生了某个网络请求。与单元测试不同E2E 测试面向的是完整应用——前端、后端和应用数据库同时参与最接近真实用户的使用路径。在 Metabase 中E2E 测试源码位于e2e/test/scenarios目录其目录结构大致镜像 Metabase 的 URL 结构。例如Admin 后台 datamodel数据模型页面的测试位于e2e/test/scenarios/admin/datamodel对应文件如 datamodel.cy.spec.ts、segments.cy.spec.ts。Metabase 的 E2E runner 会自行构建后端并创建临时的 H2 应用数据库进程被杀死时两者都会被销毁。默认保留端口为本地主机的4000。你完全可以同时在localhost:3000运行自己的本地 Metabase 实例这在调试时非常有用。提示动手之前建议先熟悉 Cypress 官方的最佳实践Best Practices本文假定读者已具备 Cypress 基础。快速开始标准开发流程Metabase 的 E2E 测试标准开发流程分两步1. 持续构建前端如果只需要前端运行bun run build-hot如果希望在 Cypress 旁边同时运行一个本地 Metabase 实例最简单的方式是bun run dev或bun run dev-ee两者底层都依赖前端热重载。dev-ee会以 Enterprise Edition 模式启动后端并以MB_EDITIONee构建前端资源见 package.json 中dev/dev-ee脚本定义。2. 在另一个终端会话中运行测试不要杀掉前一个终端另开一个会话运行bun run test-cypress这会打开 Cypress GUI让你选择要运行的测试。查看e2e/runner/run_cypress_local.ts和e2e/test/scenarios/docker-compose.yml可以了解所有可用的选项。runner 启动链路从package.json可以看到test-cypress脚本实际执行的是tsx ./e2e/runner/run_cypress_local.ts。这个 TypeScript runner 承担了完整的初始化工作解析环境变量与命令行参数MB_EDITION、CYPRESS_TESTING_TYPE、CYPRESS_GUI、GENERATE_SNAPSHOTS、JAR_PATH启动 Docker 容器docker compose -f ./e2e/test/scenarios/docker-compose.yml up -d根据是否提供JAR_PATH选择从预构建 JAR 启动后端或从源码启动带热重载的后端CypressBackend.runFromSource()生成应用数据库快照默认开启GENERATE_SNAPSHOTS会先清空e2e/support/cypress_sample_instance_data.json缓存再以无 GUI 模式运行快照生成测试检查前端是否运行在8080端口MB_FRONTEND_DEV_PORT若未运行会给出警告提示最后以e2e/support/cypress.config.js作为配置启动 Cypress。进程退出、收到SIGTERM/SIGINT时runner 会清理后端进程而 Docker 容器会保留在后台可按提示用docker compose -f ./e2e/test/scenarios/docker-compose.yml down手动停止。运行选项无头模式运行全部测试CYPRESS_GUIfalse bun run test-cypress只运行单个文件使用官方--spec标志可以快速测试单个文件也支持运行一个文件夹内的所有 specs 或多个 specCYPRESS_GUIfalse bun run test-cypress --spec e2e/test/scenarios/question/new.cy.spec.js指定浏览器使用--browser标志指定执行浏览器CYPRESS_GUIfalse bun run test-cypress --browser chrome指定浏览器在run 模式无头执行下最有意义而在open 模式GUI下可以方便地在系统所有可用浏览器之间切换不过也可以预先指定一个初始浏览器——此时它只是预选你仍然可以切换到其他浏览器。其他启动选项MB_EDITIONee默认或oss控制以哪个版本启动后端CYPRESS_TESTING_TYPEe2e默认或componentJAR_PATH指向预构建的 Metabase JAR跳过源码构建直接对 JAR 运行测试GENERATE_SNAPSHOTS是否在测试前生成快照默认为 true关闭时需留意快照缓存是否过期。这些选项都可以通过环境变量或命令行参数覆盖见 run_cypress_local.ts。测试文件结构解剖Cypress 测试文件的结构与 Mocha 一致describe块用于分组it块是具体的测试用例describe(homepage, () { it(should load the homepage and..., () { cy.visit(/metabase/url); // ... }); });推荐的元素选择方式Metabase 强烈推荐使用testing-library/cypress提供的cy.findByText()和cy.findByLabelText()这类选择器该库已在 package.json 的 devDependencies 中声明版本为^10.1.0。这类选择器鼓励写出不依赖实现细节如 CSS 类名的测试健壮性更好。避免顺带重复测试尽量通过 helper 直接跳转到目标功能而不是从首页一路点击过去。例如要测试查询构建器应直接使用openOrdersTable()这样的 helper 跳到 Orders 表而不是从首页开始依次点击 New、Question 等。测试编写技巧与常见坑containsvsfindvsgetCypress 提供了一组功能相近的元素选择命令Metabase 团队给出了以下使用建议contains默认对 DOM 中的文本区分大小写。如果匹配不到预期文本检查 CSS 是否改变了大小写可以用{ matchCase: false }选项显式忽略大小写。contains匹配的是子串。对于 filter by 和 Add a filter 两个字符串cy.contains(filter)会同时匹配两者。要避免这种误匹配可以传入固定首尾的正则表达式或将字符串限定到特定选择器cy.contains(selector, content)。find在前一个选择的结果范围内继续搜索。get即使被链式调用也默认搜索整个页面除非显式配置withinSubject选项。如何获取 Sample Database 的表与字段 IDE2E 测试使用的 Sample Database 随时可能变化表与字段的引用 ID 也随之变化。永远不要硬编码数字 ID。仓库提供了一套保证正确的机制每次启动 Cypress 时runner 都会获取 Sample Database 的信息提取表与字段 ID并写入e2e/support/cypress_sample_database.json随后通过 cypress_sample_database.js 重新导出供所有测试使用。// 不要这样写 const query { source-table: 1, aggregation: [[count]], breakout: [[field, 7, null]], }; // 应该这样写 import { SAMPLE_DATABASE } from e2e/support/cypress_sample_database; const { PRODUCTS, PRODUCTS_ID } SAMPLE_DATABASE; const query { source-table: PRODUCTS_ID, aggregation: [[count]], breakout: [[field, PRODUCTS.CATEGORY, null]], };该 JSON 文件在每次 Cypress 启动时重新生成见 default.cy.snap.js 中通过cy.writeFile(e2e/support/cypress_sample_database.json, SAMPLE_DATABASE)写入因此已被加入.gitignore。与之相关的还有 e2e/support/cypress_data.js它维护了SAMPLE_DB_TABLES表 ID 常量、USER_GROUPS与USERS测试账号等常用引用。注意该文件中的 ID 是硬编码的因此快照生成测试default.cy.snap.js中专门有ensureTableIdsAreCorrect()断言一旦实际 ID 与期望不符就会立即失败报警。加大视口避免滚动Metabase 的部分视图超过 Cypress 默认的 1280x800 视口需要滚动才能完成测试。例如虚拟化表格不会渲染视口外的内容。除非专门测试窗口 resize 行为否则不要在测试中途调用cy.viewport(width, height)而应通过 Cypress 测试配置设置视口宽高——该配置对describe和it块都生效describe(foo, { viewportWidth: 1400 }, () {}); it(bar, { viewportWidth: 1600, viewportHeight: 1200 }, () {});代码重载 vs 测试重载编辑 Cypress 测试文件时测试会自动刷新并重新运行但编辑代码文件时 Cypress 不会感知到变化。如果运行的是bun run build-hot代码会在构建后自动更新到 Cypress 中此时需要手动点击重新运行才能执行新代码。contains helper 打开时无法检查 DOMCypress 支持在测试的每一步之后使用 Chrome 检查器inspector并提供了一个辅助工具来测试contains和get调用。但该 helper 创建的新 UI 会妨碍检查器定位正确元素。如果你想在 Chrome 中检查 DOM请先关闭这个 helper。误将错误的 HTML 模板打进 Uberjarbun run build和bun run build-hot都会覆盖一个 HTML 模板以引用正确的 JavaScript 文件。如果先运行了bun run build再构建用于 Cypress 测试的 Uberjar之后即使启动bun run build-hot也看不到 JavaScript 的变更。Apple Silicon 上的问题在 Apple Silicon 处理器上运行 Cypress 可能遇到问题根因是bahmutov/cypress-esbuild-preprocessor依赖的esbuild。解决方案是使用nvm或n等 Node 版本管理器安装 NodeJS。另一个几乎必然会遇到的问题是无法连接 Mongo QA 数据库——官方支持的 Docker 镜像是 AMD64 架构与 Apple Silicon 不兼容。可通过设置以下环境变量解决export EXPERIMENTAL_DOCKER_DESKTOP_FORCE_QEMU1注意即便设置了这个变量部分用户仍会遇到 Mongo 连接超时。此时可以尝试改用 OrbStack 代替 Docker Desktop。依赖 Docker 镜像的测试由于托管环境中部分测试需要使用特权端口因此不能使用 podman 或 rootless Docker请使用经典 Docker 或 OrbStack。Metabase 有相当一部分测试依赖外部服务这些服务通过 Docker 镜像提供目前包括三个受支持的 QA 外部数据库Postgres、Mongo、MySQL、Webmail、Snowplow 和 LDAP 服务器详见 e2e/test/scenarios/docker-compose.yml。默认的 Cypress 命令会自动拉起测试所需的全部 Docker 容器你也可以手动搭建 E2E 环境但需注意会因此遇到测试失败。该 compose 文件还展示了各服务的端口映射postgres-sample5404:5432、mongo-sample27004:27017、mysql-sample3304:3306、webhook-tester9080:8080、maildev1180:1080、1125:1025、ldap389:389并额外提供了一个启用 SSL 的maildev-ssl服务需要向 Java keystore 添加根 CA 证书见maildev-keys/README.md。涉及 Snowplow 的测试依赖 Snowplow 的测试需要一个运行中的 Snowplow 服务默认已启用。你也可以手动启动 Snowplow micro Docker 容器并设置环境变量docker-compose -f ./snowplow/docker-compose.yml up -d export MB_SNOWPLOW_AVAILABLEtrue export MB_SNOWPLOW_URLhttp://localhost:9090与 Snowplow 协同测试Metabase 提供了一组处理 Snowplow 事件的测试助手源码见 e2e/support/helpers/e2e-snowplow-helpers.js每个测试前使用resetSnowplow()清空已处理事件的队列实际调用 Snowplow micro 的micro/reset接口使用expectSnowplowEvent({ ...payload }, countn)断言恰好有count个 Snowplow 事件部分匹配给定 payloadcount默认为 1使用expectUnstructuredSnowplowEvent断言恰好有count个非结构化unstructured事件部分匹配给定 payload。这是event.unstruct_event.data.data与整个event比较的便捷封装——Metabase 的绝大多数事件都是非结构化事件使用assertNoUnstructuredSnowplowEvent({ ...eventData })即expectUnstructuredSnowplowEvent(eventData, 0)断言没有非结构化事件匹配该 payload每个测试后使用expectNoBadSnowplowEvents()断言没有发送非法事件。从实现看expectSnowplowEvent会轮询 Snowplow micro 的micro/good接口间隔 100ms、超时 1000ms并做深度部分匹配仓库中大量测试文件如 search-snowplow.cy.spec.js、instance-stats-snowplow.cy.spec.js都在使用这套助手。需要 SMTP 服务器的测试部分测试依赖邮件功能需要本地 SMTP 服务器。Metabase 使用maildevDocker 镜像当前使用的镜像为maildev/maildev:2.2.1与e2e/test/scenarios/docker-compose.yml中的版本一致。默认的本地开发 Cypress 配置会自动处理手动搭建可使用docker run -d -p 1180:1080 -p 1125:1025 maildev/maildev:2.2.1E2E 使用的 maildev 刻意发布在宿主端口 1180Web UI和 1125SMTP上与开发环境使用的 1080/1025 故意不同。需要翻译字典的测试与伪语言环境翻译字典部分测试会检查内容翻译content translation功能运行这些测试前需要先执行以下命令预编译带翻译的 JSON 文件./bin/i18n/build-translation-resources伪语言环境en_ZZMetabase 提供了一个伪语言环境en_ZZ它会给所有翻译字符串加上[zz]前缀例如My text会变成[zz] My text。这在编写断言翻译工作正常的 E2E 测试时非常方便且不依赖可能随时间变化的真实翻译文本。伪语言环境的 PO 文件在构建时生成要在 UI 中使用它需以MB_ENABLE_TEST_LOCALEStrue启动后端然后在 Admin Settings Localization 中选择 English (ZZ)。善用 Cypress 内置的 LodashCypress 自带 Lodash无需将其加入直接依赖。它以下划线别名暴露方法可通过Cypress._.method()调用。可以用_.times在本地对某个测试或一组测试进行压力测试// 将测试运行 N 次 Cypress._.times(N, () { it(should foo, () { // ... }); });DB 快照机制每个测试套件开始时Metabase 会清空后端的数据库与设置缓存确保测试套件从可预测的状态开始。通常在第一个describe块内添加before(restore)即可在运行整个测试套件前恢复默认快照。如果要使用默认快照之外的快照将名称作为参数传给restorebefore(() restore(blank));也可以在beforeEach()内调用restore()以在每个测试前重置或在特定测试内调用。restore与snapshot的底层实现见 e2e-setup-helpers.jssnapshot(name)调用POST /api/testing/snapshot/{name}restore(name default)调用POST /api/testing/restore/{name}且对-writable后缀的快照会自动重置可写数据库postgres/mysql并先调用/api/testing/reset-throttlers重置限流器。当前支持的快照名包括blank、setup、without-models、default、mongo-5、postgres-12、postgres-writable、mysql-8、mysql-writable。快照是如何创建的快照由一组独立的 Cypress 测试创建。这些测试从空白数据库开始通过执行具体操作将数据库置于可预测状态。例如以 bobmetabase.com 注册、添加一个问题、打开设置 ABC 等。这些生成快照的测试扩展名为.cy.snap.js。运行时会生成数据库 dump 到frontend/tests/snapshots/*.sql。它们在测试开始前运行且不会提交到 git。以 default.cy.snap.js 为例它依次生成blank快照 → 执行 setup通过/api/setup接口完成站点初始化并缓存 admin 凭据→ 更新一系列设置如synchronous-batch-updates、enable-public-sharing、enable-embedding-sdk、embedding-secret-key等→ 生成setup快照 → 创建用户与权限组、配置权限图 → 创建集合与问题/仪表板 → 生成without-models快照 → 创建模型 → 生成default快照最后恢复blank快照。其中还包含对表 ID 正确性的断言以及将 Sample Database 元数据写入e2e/support/cypress_sample_database.json的逻辑。在 CI 中运行Cypress 会记录每次测试运行的视频便于调试此外失败的测试会保存更高质量的截图。这些文件可以在 GitHub Actions 中每次运行的 Artifacts 部分找到。针对 Enterprise Edition 运行在针对 Metabase Enterprise Edition 运行 Cypress 之前需要设置环境变量MB_EDITIONee。注意Enterprise 实例会在没有 premium token 的情况下启动如果想测试 premium 功能feature flags需要为所有 Cypress 测试提供有效 token。需要提供 4 个 tokenMB_ALL_FEATURES_TOKEN启用所有功能包括尚未向客户发布的新功能MB_STARTER_CLOUD_TOKEN仅启用 hosting 功能模拟云上的 Starter 计划MB_PRO_CLOUD_TOKEN启用 PRO 功能并加上 hosting模拟云上的 Pro 计划MB_PRO_SELF_HOSTED_TOKEN启用 PRO 功能但不含 hosting模拟 Pro 自托管计划。可以通过环境变量或cypress.env.json文件配置参考仓库中的cypress.env.json.example示例。runner 在MB_EDITIONee且缺少这些 token 时会给出警告见 run_cypress_local.ts。几个注意点如果测试开始运行但缺少 enterprise 功能请确认所用 token 已启用相应的 feature flags导航到/admin/settings/license页面时license 输入框会显示当前生效的 token分享截图时要小心泄露如果 token 看起来没问题但仍然异常可以核弹级处理运行killall java终止所有 Java 进程后重启 Cypress。测试报告每个 spec 会自动生成独立的 Mocha 报告存放在cypress/reports/mochareports。注意根级别的cypress/目录已被 git 忽略在 CI 中运行时Metabase 会做额外处理使用mochawesome-merge合并各报告、格式化并生成定制化的 GitHub Actions job summary。如果在本地也需要统一的测试报告可以调用bun run generate-cypress-html-report该脚本在 package.json 中定义为mochawesome-merge cypress/reports/mochareports/*.json cypress/reports/cypress-test-report.json marge cypress/reports/cypress-test-report.json -o cypress/reports --inline。测试标签TagsCypress 允许为测试打标签便于快速筛选某一类测试。例如可以给所有需要外部数据库的测试打上external标签然后只运行这些测试bun run test-cypress --env grepTagsexternal标签应以开头以便在搜索时与其他字符串区分。当前仓库使用的标签有external— 需要外部 Docker 容器才能运行的测试actions— 使用 Metabase actions 并在数据源中修改数据的测试。如何对 flaky 修复进行压力测试在本地修复一个 flaky不稳定测试并不代表该修复在 GitHub 的 CI 环境中有效。确保修复有效的唯一方式是在 CI 中做压力测试。这正是.github/workflows/e2e-stress-test-flake-fix.yml工作流存在的目的——它允许你在分支上快速测试修复而无需等待完整构建完成。准备创建包含修复方案的新分支并推送到远端要么完全跳过 PR要么打开一个draft草稿PR。手动触发压力测试工作流在 Use workflow from 第一个字段中选择你自己的分支这一步至关重要复制粘贴要测试的 spec 的相对路径例如e2e/test/scenarios/onboarding/urls.cy.spec.js无需加引号设置期望运行的测试次数可选按文档提供 grep 过滤条件点击绿色 Run workflow 按钮等待结果。使用该工作流的注意事项它会自动尝试查找并下载之前构建好的 Metabase uberjar以 artifact 形式存储在过去的某个 commit/CI run 中它只适用于纯 E2E 修复——不需要新的 Metabase uberjar如果修复涉及源码改动前端或后端请先开普通 PR 让 CI 跑完所有测试之后可以按上述说明手动触发压力测试工作流它会自动下载这次 CI 运行新构建的 artifact。注意 CI 必须完全跑完因为工作流通过 GitHub REST API 获取 artifact否则看不到。小结Metabase 的 E2E 测试体系是一套高度工程化的完整方案e2e/runner负责后端构建、Docker 容器、快照生成与 Cypress 启动的全流程编排e2e/support提供数据引用、命令助手、Snowplow/SMTP/翻译等特殊场景支持e2e/test/scenarios以镜像 URL 结构的目录组织数百个场景测试快照机制保证了测试的确定性与可预测性。本文覆盖了从本地开发、运行选项、测试编写规范到 CI、Enterprise 支持、flaky 修复的完整路径。掌握这套体系后你既可以快速跑通并定位失败测试也能为 Metabase 的前端功能贡献高质量的端到端测试。深入研读 e2e-tests.md、run_cypress_local.ts、cypress_data.js 与 default.cy.snap.js 等文件将帮助你进一步理解其设计精髓。【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表