
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測試體系用于對整個應用——包括前端、后端和應用數(shù)據(jù)庫——進行整體驗證。本文基于 Metabase 官方開發(fā)者指南docs/developers-guide/e2e-tests.md結合倉庫中的真實源碼e2e/目錄下的 runner、support、scenarios 等實現(xiàn)系統(tǒng)講解 E2E 測試的啟動方式、測試結構、輔助機制、快照體系、Snowplow/SMTP/翻譯字典等特殊場景以及 CI 與調試技巧幫助你快速上手并為 Metabase 編寫高質量的端到端測試。什么是 Metabase 的 E2E 測試端到端測試是運行在真實 Web 瀏覽器中的 JavaScript 腳本訪問不同的 URL、點擊各種 UI 元素、輸入文本并斷言預期行為是否發(fā)生例如界面上出現(xiàn)某個元素或發(fā)生了某個網(wǎng)絡請求。與單元測試不同E2E 測試面向的是完整應用——前端、后端和應用數(shù)據(jù)庫同時參與最接近真實用戶的使用路徑。在 Metabase 中E2E 測試源碼位于e2e/test/scenarios目錄其目錄結構大致鏡像 Metabase 的 URL 結構。例如Admin 后臺 datamodel數(shù)據(jù)模型頁面的測試位于e2e/test/scenarios/admin/datamodel對應文件如 datamodel.cy.spec.ts、segments.cy.spec.ts。Metabase 的 E2E runner 會自行構建后端并創(chuàng)建臨時的 H2 應用數(shù)據(jù)庫進程被殺死時兩者都會被銷毀。默認保留端口為本地主機的4000。你完全可以同時在localhost:3000運行自己的本地 Metabase 實例這在調試時非常有用。提示動手之前建議先熟悉 Cypress 官方的最佳實踐Best Practices本文假定讀者已具備 Cypress 基礎。快速開始標準開發(fā)流程Metabase 的 E2E 測試標準開發(fā)流程分兩步1. 持續(xù)構建前端如果只需要前端運行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腳本實際執(zhí)行的是tsx ./e2e/runner/run_cypress_local.ts。這個 TypeScript runner 承擔了完整的初始化工作解析環(huán)境變量與命令行參數(shù)MB_EDITION、CYPRESS_TESTING_TYPE、CYPRESS_GUI、GENERATE_SNAPSHOTS、JAR_PATH啟動 Docker 容器docker compose -f ./e2e/test/scenarios/docker-compose.yml up -d根據(jù)是否提供JAR_PATH選擇從預構建 JAR 啟動后端或從源碼啟動帶熱重載的后端CypressBackend.runFromSource()生成應用數(shù)據(jù)庫快照默認開啟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標志指定執(zhí)行瀏覽器CYPRESS_GUIfalse bun run test-cypress --browser chrome指定瀏覽器在run 模式無頭執(zhí)行下最有意義而在open 模式GUI下可以方便地在系統(tǒng)所有可用瀏覽器之間切換不過也可以預先指定一個初始瀏覽器——此時它只是預選你仍然可以切換到其他瀏覽器。其他啟動選項MB_EDITIONee默認或oss控制以哪個版本啟動后端CYPRESS_TESTING_TYPEe2e默認或componentJAR_PATH指向預構建的 Metabase JAR跳過源碼構建直接對 JAR 運行測試GENERATE_SNAPSHOTS是否在測試前生成快照默認為 true關閉時需留意快照緩存是否過期。這些選項都可以通過環(huán)境變量或命令行參數(shù)覆蓋見 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。這類選擇器鼓勵寫出不依賴實現(xiàn)細節(jié)如 CSS 類名的測試健壯性更好。避免順帶重復測試盡量通過 helper 直接跳轉到目標功能而不是從首頁一路點擊過去。例如要測試查詢構建器應直接使用openOrdersTable()這樣的 helper 跳到 Orders 表而不是從首頁開始依次點擊 New、Question 等。測試編寫技巧與常見坑containsvsfindvsgetCypress 提供了一組功能相近的元素選擇命令Metabase 團隊給出了以下使用建議contains默認對 DOM 中的文本區(qū)分大小寫。如果匹配不到預期文本檢查 CSS 是否改變了大小寫可以用{ matchCase: false }選項顯式忽略大小寫。contains匹配的是子串。對于 filter by 和 Add a filter 兩個字符串cy.contains(filter)會同時匹配兩者。要避免這種誤匹配可以傳入固定首尾的正則表達式或將字符串限定到特定選擇器cy.contains(selector, content)。find在前一個選擇的結果范圍內繼續(xù)搜索。get即使被鏈式調用也默認搜索整個頁面除非顯式配置withinSubject選項。如何獲取 Sample Database 的表與字段 IDE2E 測試使用的 Sample Database 隨時可能變化表與字段的引用 ID 也隨之變化。永遠不要硬編碼數(shù)字 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 中此時需要手動點擊重新運行才能執(zhí)行新代碼。contains helper 打開時無法檢查 DOMCypress 支持在測試的每一步之后使用 Chrome 檢查器inspector并提供了一個輔助工具來測試contains和get調用。但該 helper 創(chuàng)建的新 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 數(shù)據(jù)庫——官方支持的 Docker 鏡像是 AMD64 架構與 Apple Silicon 不兼容。可通過設置以下環(huán)境變量解決export EXPERIMENTAL_DOCKER_DESKTOP_FORCE_QEMU1注意即便設置了這個變量部分用戶仍會遇到 Mongo 連接超時。此時可以嘗試改用 OrbStack 代替 Docker Desktop。依賴 Docker 鏡像的測試由于托管環(huán)境中部分測試需要使用特權端口因此不能使用 podman 或 rootless Docker請使用經(jīng)典 Docker 或 OrbStack。Metabase 有相當一部分測試依賴外部服務這些服務通過 Docker 鏡像提供目前包括三個受支持的 QA 外部數(shù)據(jù)庫Postgres、Mongo、MySQL、Webmail、Snowplow 和 LDAP 服務器詳見 e2e/test/scenarios/docker-compose.yml。默認的 Cypress 命令會自動拉起測試所需的全部 Docker 容器你也可以手動搭建 E2E 環(huán)境但需注意會因此遇到測試失敗。該 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 容器并設置環(huán)境變量docker-compose -f ./snowplow/docker-compose.yml up -d export MB_SNOWPLOW_AVAILABLEtrue export MB_SNOWPLOW_URLhttp://localhost:9090與 Snowplow 協(xié)同測試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 的絕大多數(shù)事件都是非結構化事件使用assertNoUnstructuredSnowplowEvent({ ...eventData })即expectUnstructuredSnowplowEvent(eventData, 0)斷言沒有非結構化事件匹配該 payload每個測試后使用expectNoBadSnowplowEvents()斷言沒有發(fā)送非法事件。從實現(xiàn)看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中的版本一致。默認的本地開發(fā) Cypress 配置會自動處理手動搭建可使用docker run -d -p 1180:1080 -p 1125:1025 maildev/maildev:2.2.1E2E 使用的 maildev 刻意發(fā)布在宿主端口 1180Web UI和 1125SMTP上與開發(fā)環(huán)境使用的 1080/1025 故意不同。需要翻譯字典的測試與偽語言環(huán)境翻譯字典部分測試會檢查內容翻譯content translation功能運行這些測試前需要先執(zhí)行以下命令預編譯帶翻譯的 JSON 文件./bin/i18n/build-translation-resources偽語言環(huán)境en_ZZMetabase 提供了一個偽語言環(huán)境en_ZZ它會給所有翻譯字符串加上[zz]前綴例如My text會變成[zz] My text。這在編寫斷言翻譯工作正常的 E2E 測試時非常方便且不依賴可能隨時間變化的真實翻譯文本。偽語言環(huán)境的 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 會清空后端的數(shù)據(jù)庫與設置緩存確保測試套件從可預測的狀態(tài)開始。通常在第一個describe塊內添加before(restore)即可在運行整個測試套件前恢復默認快照。如果要使用默認快照之外的快照將名稱作為參數(shù)傳給restorebefore(() restore(blank));也可以在beforeEach()內調用restore()以在每個測試前重置或在特定測試內調用。restore與snapshot的底層實現(xiàn)見 e2e-setup-helpers.jssnapshot(name)調用POST /api/testing/snapshot/{name}restore(name default)調用POST /api/testing/restore/{name}且對-writable后綴的快照會自動重置可寫數(shù)據(jù)庫postgres/mysql并先調用/api/testing/reset-throttlers重置限流器。當前支持的快照名包括blank、setup、without-models、default、mongo-5、postgres-12、postgres-writable、mysql-8、mysql-writable。快照是如何創(chuàng)建的快照由一組獨立的 Cypress 測試創(chuàng)建。這些測試從空白數(shù)據(jù)庫開始通過執(zhí)行具體操作將數(shù)據(jù)庫置于可預測狀態(tài)。例如以 bobmetabase.com 注冊、添加一個問題、打開設置 ABC 等。這些生成快照的測試擴展名為.cy.snap.js。運行時會生成數(shù)據(jù)庫 dump 到frontend/tests/snapshots/*.sql。它們在測試開始前運行且不會提交到 git。以 default.cy.snap.js 為例它依次生成blank快照 → 執(zhí)行 setup通過/api/setup接口完成站點初始化并緩存 admin 憑據(jù)→ 更新一系列設置如synchronous-batch-updates、enable-public-sharing、enable-embedding-sdk、embedding-secret-key等→ 生成setup快照 → 創(chuàng)建用戶與權限組、配置權限圖 → 創(chuàng)建集合與問題/儀表板 → 生成without-models快照 → 創(chuàng)建模型 → 生成default快照最后恢復blank快照。其中還包含對表 ID 正確性的斷言以及將 Sample Database 元數(shù)據(jù)寫入e2e/support/cypress_sample_database.json的邏輯。在 CI 中運行Cypress 會記錄每次測試運行的視頻便于調試此外失敗的測試會保存更高質量的截圖。這些文件可以在 GitHub Actions 中每次運行的 Artifacts 部分找到。針對 Enterprise Edition 運行在針對 Metabase Enterprise Edition 運行 Cypress 之前需要設置環(huán)境變量MB_EDITIONee。注意Enterprise 實例會在沒有 premium token 的情況下啟動如果想測試 premium 功能feature flags需要為所有 Cypress 測試提供有效 token。需要提供 4 個 tokenMB_ALL_FEATURES_TOKEN啟用所有功能包括尚未向客戶發(fā)布的新功能MB_STARTER_CLOUD_TOKEN僅啟用 hosting 功能模擬云上的 Starter 計劃MB_PRO_CLOUD_TOKEN啟用 PRO 功能并加上 hosting模擬云上的 Pro 計劃MB_PRO_SELF_HOSTED_TOKEN啟用 PRO 功能但不含 hosting模擬 Pro 自托管計劃。可以通過環(huán)境變量或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。如果在本地也需要統(tǒng)一的測試報告可以調用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 允許為測試打標簽便于快速篩選某一類測試。例如可以給所有需要外部數(shù)據(jù)庫的測試打上external標簽然后只運行這些測試bun run test-cypress --env grepTagsexternal標簽應以開頭以便在搜索時與其他字符串區(qū)分。當前倉庫使用的標簽有external— 需要外部 Docker 容器才能運行的測試actions— 使用 Metabase actions 并在數(shù)據(jù)源中修改數(shù)據(jù)的測試。如何對 flaky 修復進行壓力測試在本地修復一個 flaky不穩(wěn)定測試并不代表該修復在 GitHub 的 CI 環(huán)境中有效。確保修復有效的唯一方式是在 CI 中做壓力測試。這正是.github/workflows/e2e-stress-test-flake-fix.yml工作流存在的目的——它允許你在分支上快速測試修復而無需等待完整構建完成。準備創(chuàng)建包含修復方案的新分支并推送到遠端要么完全跳過 PR要么打開一個draft草稿PR。手動觸發(fā)壓力測試工作流在 Use workflow from 第一個字段中選擇你自己的分支這一步至關重要復制粘貼要測試的 spec 的相對路徑例如e2e/test/scenarios/onboarding/urls.cy.spec.js無需加引號設置期望運行的測試次數(shù)可選按文檔提供 grep 過濾條件點擊綠色 Run workflow 按鈕等待結果。使用該工作流的注意事項它會自動嘗試查找并下載之前構建好的 Metabase uberjar以 artifact 形式存儲在過去的某個 commit/CI run 中它只適用于純 E2E 修復——不需要新的 Metabase uberjar如果修復涉及源碼改動前端或后端請先開普通 PR 讓 CI 跑完所有測試之后可以按上述說明手動觸發(fā)壓力測試工作流它會自動下載這次 CI 運行新構建的 artifact。注意 CI 必須完全跑完因為工作流通過 GitHub REST API 獲取 artifact否則看不到。小結Metabase 的 E2E 測試體系是一套高度工程化的完整方案e2e/runner負責后端構建、Docker 容器、快照生成與 Cypress 啟動的全流程編排e2e/support提供數(shù)據(jù)引用、命令助手、Snowplow/SMTP/翻譯等特殊場景支持e2e/test/scenarios以鏡像 URL 結構的目錄組織數(shù)百個場景測試快照機制保證了測試的確定性與可預測性。本文覆蓋了從本地開發(fā)、運行選項、測試編寫規(guī)范到 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創(chuàng)作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考