蒼時弦也
蒼時弦也
資深軟體工程師
發表於

前端環境:Vite 與 Cucumber - Cucumber 的文件測試法

這篇文章是 Cucumber 的文件測試法 系列的一部分。

因為 Cucumber 是能夠跨越不同開發環境使用的,因此這次我們會以前端與後端互相搭配的方式進行,在前端的部分採用 Vite 和 Playwright 來進行測試。

Vite 安裝

Vite 是非常輕量的工具,也有 Vite Ruby 可以跟 Ruby 的專案一起使用,這一次我們會使用 Vue 與 TypeScript 和 Cucumber 搭配開發,因此我們需要先建立一個前端的專案供我們使用。

1yarn create frontend --template vue-ts

如果習慣使用 npm 或者 pnpm 等工具,也可以依照官方文件的指示選擇使用自己習慣的套件管理工具。

Cucumber 安裝

要安裝 Cucumber 只需要透過套件管理工具加入到 package.json 即可,除了 Cucumber 之外,我們還希望有 Playwright 來模擬使用真實瀏覽器操作的狀況,因此一起將兩個套件加入。

1yarn add --dev @cucumber/cucumber playwright @playwright/test

Playwright 本身就有運行測試的功能,因為我們希望讓 Cucumber 來扮演這個角色,因此需要另外加入 @playwright/test 這個套件,用來實現斷言相關的機制。

完成之後我們可以簡單用 yarn run 命令驗證 Cucumber 正常運作。

1yarn run cucumber-js

接下來我們要將 Cucumber 設定為會使用 Playwright 開啟瀏覽器以及 Vite 預覽伺服器的模式,來讓我們可以測試到真實的 Vite 專案。

環境設定

Cucumber 針對前端的測試基本上有幾種方式,比較直覺的做法是先進行 yarn build(建置)之後直接使用靜態的檔案處理,另外一個就是我們直接利用 Vite 的 createServer API 啟動一個測試用的環境來運行。

首先,我們先在專案目錄下加入 cucumber.js 來設定我們的測試環境。

1export default {
2  publishQuiet: true,
3  requireModule: ['tsm'],
4  require: [
5    'features/**/*.ts'
6  ],
7  strict: true,
8  tags: 'not @wip',
9}

為了讓 TypeScript 可以被 Cucumber 辨識到,我們可以使用 requireModule 的選項載入對應的模組,像是 tsm 或者 ts-node 等等,可以依照自己的偏好選擇。

接下來要讓 Playwright 在測試開始時啟動瀏覽器,以及使用 Vite 的 API 來運行一個測試用的環境,我們加入 features/support/world.ts 這個檔案來進行定義。

因為用 features/**/*.ts 定義了要自動載入的路徑,我們這邊使用大多數 Cucumber 的慣例將環境設定相關的檔案放到 features/support 目錄下

 1import { Before, After, AfterAll, setWorldConstructor } from "@cucumber/cucumber"
 2import { createServer, ViteDevServer } from "vite"
 3import * as playwright from 'playwright'
 4
 5// 啟動瀏覽器與建立伺服器
 6const browser = playwright.chromium.launch()
 7const server = createServer({ mode: "test" })
 8
 9export default class World {
10  // 紀錄目前測試的狀態
11  context: playwright.BrowserContext
12  page: playwright.Page
13  server: ViteDevServer
14
15  async init() {
16    // 啟動 Vite 伺服器
17    this.server = await (await server).listen()
18    // 建立新的瀏覽器設定檔,並且設定網址基準為 Vite 伺服器位置
19    this.context = await (await browser).newContext({
20      baseURL: this.server.resolvedUrls?.local[0],
21    });
22    // 開啟一個新的分頁
23    this.page = await this.context.newPage()
24  }
25
26  // 開啟特定路徑
27  async visit(path: string) {
28    await this.page.goto(path)
29  }
30}
31
32// 設定 Cucumber 使用 World 這個類別運行測試
33setWorldConstructor(World);
34
35// 在測試開始前進行初始化
36Before(async function() {
37  await this.init()
38})
39
40// 結束後關閉分頁與測試伺服器
41After(async function() {
42  await this.context.close()
43  await this.server.close()
44})
45
46// 所有測試結束後關閉瀏覽器
47AfterAll(async function() {
48  await (await browser).close()
49})

上面這段程式碼有點複雜,我們需要自己管理 Playwright 開啟的瀏覽器以及 Vite 的測試環境,同時每個測試都開啟新的瀏覽器也不太現實,因此會統一開啟一個瀏覽器直到所有測試都結束為止。

在 Playwright 的概念中,瀏覽器本身還有設定檔(BrowserContext)的功能,用 Chrome 舉例就是「個人資料」的機制,每個測試都開啟獨立的 BrowserContext 可以確保不被 Cookie 等紀錄影響。

設定完畢後,我們就可以著手於撰寫 features/hello.featurefeatures/step_definitions/common.ts 這兩個檔案,來驗證我們的測試可以順利運行。

1Feature: Hello World
2  Scenario: I can test with Playwright
3    Then I can see "Vite"
1import { Then } from "@cucumber/cucumber"
2import { expect } from "@playwright/test"
3
4Then('I can see {string}', async function (string) {
5  await this.visit('/')
6  await expect(this.page).toHaveTitle(new RegExp(string))
7});

為了讓測試簡單被理解,我們可以做一個對網頁標題的檢查,這裡就可以直接引用 Playwright 的 expect 方法來進行斷言,這個好處是我們能直接使用到所有 Playwright 的測試 API,就不需要自己再額外的實作各種尋找 HTML 元素的行為。

現在運行 yarn run cucumber-js 就可以看到測試成功的結果。

1yarn run v1.22.19
2$ /Users/elct9620/Workspace/StarPortal/Cucumber2024/frontend/node_modules/.bin/cucumber-js
3...
4
51 scenario (1 passed)
61 step (1 passed)
70m01.678s (executing steps: 0m01.632s)
8✨  Done in 3.13s.

在 JavaScript 的生態系中測試框架的斷言有時候是能夠混用的,以 Vite 經常搭配使用的 Vitest 為例子,我們也可以用 @playwright/test 套件來取代原本的斷言功能,這樣就可以很輕鬆的用 Playwright 來實現 E2E Testing 而不需要使用多款工具搭配使用。