博客 / 詳情

返回

基於 React 和 Vite 的前端項目自動化測試方案

背景

前端自動化測試在工程化的研發體系中不可或缺。前端領域的自動化測試常被忽略,原因在於人們認為編寫測試用例成本高且意義不大,本質是覺得投資回報率低。但當收益大於支出時,引入前端自動化測試是必要的。例如在表單功能從簡單到複雜的迭代過程中,手動測試成本會指數級增長且可能無法完成所有測試,此時引入自動化測試能提升效率、保證測試覆蓋範圍、減少誤差和遺漏、實現用例重複使用等。

成本

  1. 初始成本,引入自動化測試框架,首次適配項目的開發成本。
  2. 新用例的編寫成本,每次增加新測試用例都需要編寫。
  3. 項目處在初期,迭代頻率高,或者用例編寫不合理時,自動化測試用例可能存在脆弱性,反而降低測試效率。

收益

  1. 手動測試的成本減少,自動化測試用例可取代大部分手動測試。
  2. 整體的測試速度更快,能縮短需求的迭代週期,提升效率。
  3. 測試的覆蓋範圍更大,新功能上線時能保證所有存量功能被覆蓋。
  4. 誤差和遺漏更少,避免人工失誤和錯誤。
  5. 用例可重複使用,在項目多個迭代的多個生命週期裏保證測試邏輯的高度一致性。

相關概念

總結了一下,前端測試大概分為 4 類:

  1. 靜態檢查(Static),比如 eslint 和 typescript 所處理的類型或語法錯誤,這部分一直在使用,基本保證了第三方庫升級或小型重構後在解決完報錯以後直接使用。這部分也是目前團隊裏面已經 cover 到的部分,在 commit 和 ci 中都有做靜態檢查。
  2. 單元測試(Unit) 用來驗證獨立的代碼片段能否正常工作。
  3. 集成測試(Integration) 用來驗證多個單元能否一起工作。
  4. 端對端測試(End to End) 模擬真實用户對應用程序操作,又叫做功能性測試。

以上測試的覆蓋範圍依次減小,並且單元測試和集成測試之間的區別可能很難區分,正如React官網所言:

對組件來説,“單元測試”和“集成測試”之間的差別可能會很模糊。如果你在測試一個表單,用例是否應該也測試表單裏的按鈕呢?一個按鈕組件又需不需要有他自己的測試套件?重構按鈕組件是否應該影響表單的測試用例?不同的團隊或產品可能會得出不同的答案。

由於靜態檢查部分已經完成,因此在我們後面的實踐裏,主要做剩餘三個類型的測試:單元測試(工具函數 utils 單測),集成測試(後面都叫他組件測試,通用 UI 組件單測),和E2E 測試。

技術選型

單元測試 Jest vs Vitest

功能特性 Jest Vitest
經大型公司實戰檢驗
模塊支持
支持
瀏覽器 UI
類型測試
基準測試
源代碼測試
瀏覽器模式
快照測試
交互快照測試
代碼覆蓋率
併發測試
分片支持
多項目運行器
模擬

結論

經過實踐,Jest 對 TS 的支持更加繁瑣,要配合 babel 之類實現,其次是它對 vite 也不友好,官網中也有説明這點, 所以最終選擇了 Vitest。

Jest can be used in projects that use vite to serve source code over native ESM to provide some frontend tooling, vite is an opinionated tool and does offer some out-of-the box workflows. Jest is not fully supported by vite due to how the plugin system from vite works, but there are some working examples for first-class jest integration using vite-jest, since this is not fully supported, you might as well read the limitation of the vite-jest. Refer to the vite guide to get started.

組件測試 Playwright vs RTL

Playwright

Playwright Component testing 相對較新,被標記為 Playwright 的實驗性功能,於2022年1.22.0版本發佈實施。在此之前,測試人員必須使用其他框架比如 RTL 來測試他們應用的組件。

React Testing Library(RTL)

RTL 是一個專門為測試 React 組件而設計的測試工具,它的解決方案主要是測試時通過像用户使用一樣,查找 dom 元素並與其交互的方式然後驗證的方式來進行測試的,應避免測試實現細節,避免重構(修改實現但不修改功能)時造成測試用例失效。

結論

我這邊是想讓依賴的儘可能少且統一,暫定使用 e2e 框架 playwright 來實現。

E2E 測試 Playwright vs Cypress

Cypress Playwright
TypeScript 支持 從 4.4.0 版本開始提供,配置簡單,自身 API 有較好的 TS 類型支持,自定義 Commands 支持度一般,需自己寫.d.ts 文件 開箱即用,TS 開發體驗極佳
Authentication 鑑權 - 基本用法 可通過 UI 或 API 獲取鑑權信息 通過 UI 或 API 獲取鑑權信息
Authentication 鑑權 - 鑑權複用 使用 cy.session 抓取頁面 session 並緩存,避免重複登錄 配置 globalSetup,抓取鑑權信息保存到本地複用
Authentication 鑑權 - 角色切換 不支持多 tab 同時運行,所有切換在一個 tab 下進行 可以在一個用例中,打開多個窗口進行不同角色賬號操作,互不影響
Hover 事件支持 事件觸發模擬,不支持 Hover ,需藉助社區庫實現 完美支持
拖拽 滿足基本拖拽需求 拖拽相關 API 豐富
文件上傳、下載 可通過庫實現上傳,官網無上傳文檔説明 提供多種靈活的上傳方式
iframe 支持 支持,API 易用性一般 相關 API 基本與普通頁面一致,使用簡單且功能不受限
多 Tab 支持 不支持多個 Tab 同時運行 完美支持
網絡請求 支持攔截請求前後,發起請求 支持攔截並修改請求、代理請求、發起請求,可準確控制監聽觸發請求
斷言 內部捆綁 Chai 斷言庫,提供 automatically retry 使用 jest 的 expect,提供特有斷言方法和 re-testing 特性
報告與調試 - 報告 自身報告格式有限,社區插件提供多樣報告;通過查看 video 與頁面快照調試,本地可用 cypress open mode 調試 內置多種格式報告支持,HTML 報告體驗好;通過查看 video 與 trace 調試,在 VS Code 中可用 Playwright Test for VSCode 調試
語法 更接近於 JQuery 語法風格,大量鏈式調用及全局命令注入 更接近於現代 JavaScript & TypeScript 語法風格,可選擇面向對象或函數式編碼風格
併發執行支持 官方不提供單個機器中多個瀏覽器併發運行用例能力,可通過社區插件實現;提供多個機器中併發執行能力,但需配合 Cypress Dashboard 服務 在併發執行方面支持度好,可在相同與不同文件、多種瀏覽器中併發執行
開源支持 - 收費 開源免費,DashBoard 服務免費版有功能與使用數量限制,無法私有化部署 開源且完全免費
開源支持 - 社區生態 有良好插件生態,但部分插件可能未及時維護 無插件生態,官方提供穩定可靠的各種能力
持續集成 - 基本使用 使用官方提供的 docker 鏡像與 github-action,使用 JamesIves/github-pages-deploy-action 部署報告,使用 actions/upload-artifact 歸檔 使用 mcr.microsoft.com/playwright 鏡像,使用 JamesIves/github-pages-deploy-action 部署報告,使用 actions/upload-artifact 歸檔
持續集成 - 併發執行 官方提供的併發執行需配合 Cypress dashboard,併發執行時用例拆分策略無法控制 通過 matrix strategies 定義變量實現併發執行

總結

經過實踐,Cypress 在 API 可讀性方面我個人覺得是優於 Playwright 的,不過這個觀點仁者見仁智者見智,Cypress 的 api 要更接近於 jquery,並且它不用寫 async await,但是它有部分功能需要付費才能使用,而 Playwright 相當於開箱即用了,什麼都有,並且性能更優還完全免費。
總體而言 Playwright 是要更優於 Cypress 的方案的,我們也將在項目中採取這種方案。

項目準備

升級 node 版本

前端統一升級 node 版本到 v20.10.0版本,因為 jsdom(在單元測試中需要用到,他能提供真實的瀏覽器環境,比如在測試一些下載功能跟瀏覽器相關的功能時是必須的)要求 node 版本不低於 20x。

1.13 更新:暫時不必升級 node 版本,如果遇到 error lru-cache@11.0.2: The engine "node" is incompatible with this module. Expected version "20 || >=22". Got "18.18.0"報錯,請參閲此 issue,在 package.jon添加此配置:

"resolutions": {
  "@asamuzakjp/css-color": "^2.8.3-b.1"
}

更新 script 腳本

為本地測試和 CI 做準備,參考:

{
  "script": {
    "test:e2e:ci": "playwright test",
    "test:unit": "vitest",
    "test:unit:ci": "vitest run",
    "coverage": "vitest run --coverage"
  }
}

升級基礎設施前端公共庫

目前需要升級的有 @core/utils@core/fetch 兩個庫,最新版本調整了構建產物,修改了 module 配置使 fetch 包構建後產物包含 index.js 以便項目中 vitest 能正確識別。

新增的依賴庫

注意:測試相關的依賴基本都是開發依賴,所以安裝在 devDependencies 下面:

{
  "@types/jsdom": "^21.1.7",
  "jsdom": "^26.0.0",
  "vitest": "^2.1.8",
  "@playwright/test": "^1.49.1",
  "playwright": "^1.49.1"
}

更新 vite 配置

在原來的 vite.config.js 中新增 test 字段, 其中 test.include 應該根據你實際的目錄來決定。

{
  "test": {
    "environment": "jsdom",
    "include": ["./tests/unit/*.test.ts"]
  }
}

新增 playwright 配置文件

這裏給一份比較初始的配置,主要就是改了一下指定測試目錄和需要在哪些瀏覽器下測試,具體的配置信息請參閲文檔。

import { defineConfig, devices } from '@playwright/test'

/**
 * Read environment variables from file.
 * https://github.com/motdotla/dotenv
 */
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });

/**
 * See https://playwright.dev/docs/test-configuration.
 */
export default defineConfig({
  // 測試目錄
  testDir: './tests/e2e',
  // 是否併發運行測試
  fullyParallel: true,
  /* Fail the build on CI if you accidentally left test.only in the source code. */
  forbidOnly: !!process.env.CI,
  // 測試失敗用例重試次數
  retries: process.env.CI ? 2 : 0,
  // 測試時使用的進程數,進程數越多可以同時執行的測試任務就越多。不設置則儘可能多地開啓進程。
  workers: process.env.CI ? 1 : undefined,
  // 指定測試結果如何輸出
  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
  reporter: 'html',
  // 測試 project 的公共配置,會與與下面 projects 字段中的每個對象的 use 對象合併。
  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
  use: {
    // 測試時各種請求的基礎路徑
    /* Base URL to use in actions like `await page.goto('/')`. */
    // baseURL: 'http://127.0.0.1:3000',

    // 生成測試追蹤信息的規則,on-first-retry 意為第一次重試時生成。
    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
    trace: 'on-first-retry'
  },
  // 定義每個 project,示例中將不同的瀏覽器測試區分成了不同的項目
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] }
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] }
    }
  ]
})

風格

tests/
├── component/ # 組件測試目錄
│ ├── Address.spec.tsx # 組件測試文件示例
│ └── stories/ # 組件目錄
│     └── Address.stories.tsx # 組件文件示例
│
├── e2e/ # 端到端測試目錄
│ └── login.spec.ts # E2E測試文件示例
│
├── playwright/ # Playwright 配置目錄
│ └── index.tsx # 測試模板文件
│
├── unit/ # 單元測試目錄
│ └── array.test.ts # 單元測試文件示例
│
├── playwright-ct.config.ts # Playwright 組件測試配置文件
└── playwright.config.ts # Playwright 端到端測試配置文件

每個目錄的主要用途:

  • component/: 存放組件測試文件,使用 Playwright 進行組件測試
  • component/stories/: 存放組件故事文件,用於組件測試場景定義
  • playwright/: 存放 Playwright 相關配置和模板
  • unit/: 存放普通單元測試文件
user avatar chongdianqishi 頭像 buxia97 頭像 waweb 頭像 frontoldman 頭像 nihaojob 頭像 shengmingbuxi_5c1152527848f 頭像 wjchumble 頭像 ni_5e1946a1c2171 頭像 643104191 頭像 diyxiaoshitou 頭像
10 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.