測試執行器
Storybook 測試執行器會將您的所有 Story 轉換為可執行的測試。它由 Jest 和 Playwright 提供支援。
- 對於那些沒有 play 函式的 Story:它會驗證 Story 是否在沒有任何錯誤的情況下渲染。
- 對於那些具有 play 函式的 Story:它還會檢查 play 函式中的錯誤,並檢查是否所有斷言都通過了。
這些測試會在即時瀏覽器中執行,並且可以透過命令列或您的 CI 伺服器執行。
設定
測試執行器是一個獨立的、與框架無關的公用程式,它與您的 Storybook 並行執行。您需要採取一些額外的步驟才能正確設定它。以下詳細說明我們建議的設定和執行方式。
執行以下命令來安裝它。
npm install @storybook/test-runner --save-dev
更新您的 package.json
指令碼,並啟用測試執行器。
{
"scripts": {
"test-storybook": "test-storybook"
}
}
使用以下方式啟動您的 Storybook
npm run storybook
Storybook 的測試執行器需要本機執行的 Storybook 執行個體或已發佈的 Storybook,才能執行所有現有的測試。
最後,開啟一個新的終端機視窗,並使用以下命令執行測試執行器
npm run test-storybook
設定
測試執行器為 Storybook 提供零配置支援。不過,您可以執行 test-storybook --eject
來獲得更精細的控制。它會在專案的根目錄產生 test-runner-jest.config.js
檔案,您可以修改此檔案。此外,您可以擴充產生的組態檔案,並提供 testEnvironmentOptions,因為測試執行器在底層也使用 jest-playwright。
CLI 選項
測試執行器由 Jest 提供支援,並接受其 CLI 選項的子集(例如,--watch
、--maxWorkers
)。如果您已在專案中使用任何這些旗標,您應該能夠將它們遷移到 Storybook 的測試執行器中,而不會有任何問題。以下列出所有可用的旗標以及使用它們的範例。
選項 | 描述 |
---|---|
--help | 輸出使用方式資訊test-storybook --help |
-s 、--index-json | 以索引 json 模式執行。自動偵測(需要相容的 Storybook)test-storybook --index-json |
--no-index-json | 停用索引 json 模式test-storybook --no-index-json |
-c 、--config-dir [dir-name] | 從中載入 Storybook 設定的目錄test-storybook -c .storybook |
--watch | 以監看模式執行test-storybook --watch |
--watchAll | 監看檔案的變更,並在發生變更時重新執行所有測試。test-storybook --watchAll |
--coverage | 在您的 Story 和元件上執行覆蓋率測試test-storybook --coverage |
--coverageDirectory | 寫入覆蓋率報告輸出的目錄test-storybook --coverage --coverageDirectory coverage/ui/storybook |
--url | 定義執行測試的 URL。適用於自訂 Storybook URLtest-storybook --url http://the-storybook-url-here.com |
--browsers | 定義執行測試的瀏覽器。可以是以下一個或多個:chromium、firefox、webkittest-storybook --browsers firefox chromium |
--maxWorkers [數量] | 指定工作執行緒集區為執行測試而產生之工作執行緒的最大數量test-storybook --maxWorkers=2 |
--testTimeout [數量] | 定義測試在自動標示為失敗之前可以執行的最長時間 (以毫秒為單位)。適用於長時間執行的測試test-storybook --testTimeout=60000 |
--no-cache | 停用快取test-storybook --no-cache |
--clearCache | 刪除 Jest 快取目錄,然後結束而不執行測試test-storybook --clearCache |
--verbose | 以測試套件階層結構顯示個別測試結果test-storybook --verbose |
-u , --updateSnapshot | 使用此標記來重新錄製本次測試執行期間失敗的每個快照test-storybook -u |
--eject | 建立本機設定檔,以覆寫測試執行器的預設值test-storybook --eject |
--json | 以 JSON 格式列印測試結果。此模式會將所有其他測試輸出和使用者訊息傳送到 stderr。test-storybook --json |
--outputFile | 當也指定了 --json 選項時,將測試結果寫入檔案。test-storybook --json --outputFile results.json |
--junit | 表示應在 junit 檔案中報告測試資訊。test-storybook --**junit** |
--ci | 它不會自動儲存新的快照,而是會讓測試失敗,並要求使用 --updateSnapshot 執行 Jest。test-storybook --ci |
--shard [index/count] | 需要 CI。將測試套件的執行分割到多個機器上test-storybook --shard=1/8 |
--failOnConsole | 讓測試在瀏覽器主控台發生錯誤時失敗test-storybook --failOnConsole |
--includeTags | 實驗性功能 定義要測試的故事子集,如果它們符合啟用的標籤。 test-storybook --includeTags="test-only, pages" |
--excludeTags | 實驗性功能 如果故事符合提供的標籤,則會防止測試這些故事。 test-storybook --excludeTags="no-tests, tokens" |
--skipTags | 實驗性功能 將測試執行器設定為跳過執行符合提供的標籤的故事的測試。 test-storybook --skipTags="skip-test, layout" |
npm run test-storybook -- --watch
針對已部署的 Storybook 執行測試
依預設,測試執行器假設您正在針對本機伺服器上的 Storybook,連接埠為 6006
執行測試。如果您想要定義目標 URL 以針對已部署的 Storybook 執行測試,您可以使用 --url
標記
npm run test-storybook -- --url https://the-storybook-url-here.com
或者,您可以設定 TARGET_URL
環境變數並執行測試執行器
TARGET_URL=https://the-storybook-url-here.com yarn test-storybook
設定 CI 以執行測試
您也可以設定測試執行器在 CI 環境中執行測試。以下是一些可以幫助您入門的秘訣。
透過 Github Actions 部署針對已部署的 Storybook 執行測試
如果您使用 Vercel 或 Netlify 等服務發佈您的 Storybook,它們會在 GitHub Actions 中發出 deployment_status
事件。您可以使用它,並將 deployment_status.target_url
設定為 TARGET_URL
環境變數。以下說明如何操作
# .github/workflows/storybook-tests.yml
name: Storybook Tests
on: deployment_status
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
if: github.event.deployment_status.state == 'success'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Install dependencies
run: yarn
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run Storybook tests
run: yarn test-storybook
env:
TARGET_URL: '${{ github.event.deployment_status.target_url }}'
發佈的 Storybook 必須公開可用,此範例才能運作。如果需要驗證,我們建議使用下方的秘訣來執行測試伺服器。
針對未部署的 Storybook 執行測試
您可以使用您的 CI 提供者(例如,GitHub Actions、GitLab Pipelines、CircleCI)來建置和執行測試執行器,以針對您建置的 Storybook 執行測試。以下是一個依賴協力廠商程式庫的秘訣,也就是說,concurrently、http-server 和 wait-on 來建置 Storybook 並使用測試執行器執行測試。
# .github/workflows/storybook-tests.yml
name: 'Storybook Tests'
on: push
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Install dependencies
run: yarn
- name: Install Playwright
run: npx playwright install --with-deps
- name: Build Storybook
run: yarn build-storybook --quiet
- name: Serve Storybook and run tests
run: |
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
"npx http-server storybook-static --port 6006 --silent" \
"npx wait-on tcp:127.0.0.1:6006 && yarn test-storybook"
依預設,Storybook 會將建置輸出到 storybook-static
目錄。如果您使用的是不同的建置目錄,則需要相應地調整秘訣。
Chromatic 和測試執行器有何差異?
測試執行器是一種通用測試工具,可以在本機或 CI 上執行,並且可以設定或擴充以執行各種測試。
Chromatic 是一個雲端服務,可執行視覺和元件測試(以及即將推出的協助工具測試),而無需設定測試執行器。它也會與您的 git 提供者同步,並管理私人專案的存取控制。
但是,在某些情況下,您可能想要將測試執行器和 Chromatic 配對使用。
- 在本機上使用,並在您的 CI 上使用 Chromatic。
- 使用 Chromatic 進行視覺和元件測試,並使用測試執行器執行其他自訂測試。
進階設定
測試掛勾 API
測試執行器會轉譯一個故事,並執行其play 函式(如果有的話)。但是,某些行為無法透過在瀏覽器中執行的 play 函式來達成。例如,如果您想要讓測試執行器為您拍攝視覺快照,這可以透過 Playwright/Jest 實現,但必須在 Node 中執行。
測試執行器會匯出可以全域覆寫的測試掛勾,以啟用視覺或 DOM 快照等使用案例。這些掛勾可讓您在轉譯故事之前和之後存取測試生命週期。以下列出可用的掛勾,以及如何使用它們的概觀。
掛勾 | 描述 |
---|---|
prepare | 準備瀏覽器進行測試async prepare({ page, browserContext, testRunnerConfig }) {} |
setup | 在所有測試執行之前執行一次setup() {} |
preVisit | 在首次瀏覽故事並在瀏覽器中轉譯之前執行async preVisit(page, context) {} |
postVisit | 在瀏覽並完全轉譯故事後執行async postVisit(page, context) {} |
這些測試掛勾是實驗性的,可能會隨時變更。我們鼓勵您盡可能在故事的 play 函式中進行測試。
若要啟用掛勾 API,您需要在 Storybook 目錄中新增一個設定檔,並依照以下方式進行設定
import type { TestRunnerConfig } from '@storybook/test-runner';
const config: TestRunnerConfig = {
// Hook that is executed before the test runner starts running tests
setup() {
// Add your configuration here.
},
/* Hook to execute before a story is initially visited before being rendered in the browser.
* The page argument is the Playwright's page object for the story.
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async preVisit(page, context) {
// Add your configuration here.
},
/* Hook to execute after a story is visited and fully rendered.
* The page argument is the Playwright's page object for the story
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async postVisit(page, context) {
// Add your configuration here.
},
};
export default config;
除了 setup
函式之外,所有其他函式都會非同步執行。preVisit
和 postVisit
函式都包含兩個額外引數,即 Playwright 頁面和一個內容物件,其中包含故事的 id
、title
和 name
。
當測試執行器執行時,您現有的測試會經歷以下生命週期
- 會在所有測試執行之前執行
setup
函式。 - 會產生包含所需資訊的內容物件。
- Playwright 會瀏覽至故事的頁面。
- 會執行
preVisit
函式。 - 會轉譯故事,並執行任何現有的
play
函式。 - 會執行
postVisit
函式。
(實驗性)篩選測試
當你在 Storybook 上執行測試工具時,預設會測試每個 story。然而,如果你想篩選測試,可以使用 tags
配置選項。 Storybook 最初引入此功能是為了產生 story 的自動文件。但是,它可以進一步擴展,以使用類似的配置選項或透過 CLI 標誌(例如,--includeTags
、--excludeTags
、--skipTags
)來配置測試工具,以根據提供的標籤執行測試,這些標誌僅在最新的穩定版本(0.15
或更高版本)中可用。下面列出了可用的選項以及如何使用它們的概述。
選項 | 描述 |
---|---|
排除 (exclude) | 防止符合所提供標籤的 story 被測試。 |
包含 (include) | 定義一個 story 的子集,只有當它們符合啟用的標籤時才進行測試。 |
跳過 (skip) | 如果 story 符合所提供的標籤,則跳過該 story 的測試。 |
import type { TestRunnerConfig } from '@storybook/test-runner';
const config: TestRunnerConfig = {
tags: {
include: ['test-only', 'pages'],
exclude: ['no-tests', 'tokens'],
skip: ['skip-test', 'layout'],
},
};
export default config;
使用 CLI 標誌執行測試的優先級高於在設定檔中提供的選項,並且會覆蓋設定檔中的可用選項。
停用測試
如果你想防止測試工具測試特定的 story,你可以使用自訂標籤配置你的 story,將其啟用到測試工具的設定檔中,或者使用 --excludeTags
CLI 標誌執行測試工具,並將它們排除在測試之外。當你想排除尚未準備好進行測試或與你的測試無關的 story 時,這很有幫助。例如:
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta: Meta<typeof MyComponent> = {
component: MyComponent,
tags: ['no-tests'], // 👈 Provides the `no-tests` tag to all stories in this file
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const ExcludeStory: Story = {
//👇 Adds the `no-tests` tag to this story to exclude it from the tests when enabled in the test-runner configuration
tags: ['no-tests'],
};
為 story 的子集執行測試
若要允許測試工具僅在特定 story 或 story 的子集上執行測試,你可以使用自訂標籤配置 story,在測試工具的設定檔中啟用它,或者使用 --includeTags
CLI 標誌執行測試工具,並將它們包含在你的測試中。例如,如果你想根據 test-only
標籤執行測試,你可以如下調整你的設定:
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta: Meta<typeof MyComponent> = {
component: MyComponent,
tags: ['test-only'], // 👈 Provides the `test-only` tag to all stories in this file
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const IncludeStory: Story = {
//👇 Adds the `test-only` tag to this story to be included in the tests when enabled in the test-runner configuration
tags: ['test-only'],
};
為元件的 story 應用標籤應在元件層級 (使用 meta
) 或在 story 層級完成。 Storybook 不支援在 story 之間導入標籤,且不會如預期般運作。
跳過測試
如果你想跳過在特定 story 或 story 子集上執行測試,你可以使用自訂標籤配置你的 story,在測試工具的設定檔中啟用它,或使用 --skipTags
CLI 標誌執行測試工具。使用此選項執行測試會導致測試工具忽略這些測試,並在測試結果中相應地標記它們,表示這些測試已暫時停用。例如:
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta: Meta<typeof MyComponent> = {
component: MyComponent,
tags: ['skip-test'], // 👈 Provides the `skip-test` tag to all stories in this file
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const SkipStory: Story = {
//👇 Adds the `skip-test` tag to this story to allow it to be skipped in the tests when enabled in the test-runner configuration
tags: ['skip-test'],
};
已部署 Storybook 的身份驗證
如果你使用需要身份驗證的安全託管服務供應商來託管你的 Storybook,你可能需要設定 HTTP 標頭。這主要是因為測試工具如何透過 fetch 請求和 Playwright 檢查實例的狀態及其 story 的索引。要執行此操作,你可以修改測試工具的設定檔以包含 getHttpHeaders
函數。此函數將 fetch 呼叫和頁面訪問的 URL 作為輸入,並返回一個包含需要設定的標頭的物件。
import type { TestRunnerConfig } from '@storybook/test-runner';
const config: TestRunnerConfig = {
getHttpHeaders: async (url) => {
const token = url.includes('prod') ? 'prod-token' : 'dev-token';
return {
Authorization: `Bearer ${token}`,
};
},
};
export default config;
輔助函數
測試工具匯出了一些輔助函數,可以透過存取 Storybook 的內部元件(例如,args
、parameters
)來使你的測試更具可讀性和可維護性。下面列出了可用的輔助函數以及如何使用它們的概述。
import type { TestRunnerConfig } from '@storybook/test-runner';
import { getStoryContext, waitForPageReady } from '@storybook/test-runner';
const config: TestRunnerConfig = {
// Hook that is executed before the test runner starts running tests
setup() {
// Add your configuration here.
},
/* Hook to execute before a story is initially visited before being rendered in the browser.
* The page argument is the Playwright's page object for the story.
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async preVisit(page, context) {
// Add your configuration here.
},
/* Hook to execute after a story is visited and fully rendered.
* The page argument is the Playwright's page object for the story
* The context argument is a Storybook object containing the story's id, title, and name.
*/
async postVisit(page, context) {
// Get the entire context of a story, including parameters, args, argTypes, etc.
const storyContext = await getStoryContext(page, context);
// This utility function is designed for image snapshot testing. It will wait for the page to be fully loaded, including all the async items (e.g., images, fonts, etc.).
await waitForPageReady(page);
// Add your configuration here.
},
};
export default config;
使用測試工具存取 story 資訊
如果你需要存取關於 story 的資訊,例如其參數,測試工具包含一個名為 getStoryContext
的輔助函數,你可以用它來檢索資訊。然後,你可以根據需要使用它來進一步自訂你的測試。例如,如果你需要設定 Playwright 頁面的viewport 大小以使用 story 參數中定義的 viewport 大小,你可以按照以下方式操作:
import type { TestRunnerConfig } from '@storybook/test-runner';
import { getStoryContext } from '@storybook/test-runner';
const { MINIMAL_VIEWPORTS } = require('@storybook/addon-viewport');
const DEFAULT_VIEWPORT_SIZE = { width: 1280, height: 720 };
const config: TestRunnerConfig = {
async preVisit(page, story) {
// Accesses the story's parameters and retrieves the viewport used to render it
const context = await getStoryContext(page, story);
const viewportName = context.parameters?.viewport?.defaultViewport;
const viewportParameter = MINIMAL_VIEWPORTS[viewportName];
if (viewportParameter) {
const viewportSize = Object.entries(viewportParameter.styles).reduce(
(acc, [screen, size]) => ({
...acc,
// Converts the viewport size from percentages to numbers
[screen]: parseInt(size),
}),
{},
);
// Configures the Playwright page to use the viewport size
page.setViewportSize(viewportSize);
} else {
page.setViewportSize(DEFAULT_VIEWPORT_SIZE);
}
},
};
export default config;
使用資源
如果你正在執行一組特定的測試(例如,影像快照測試),測試工具提供了一個名為 waitForPageReady
的輔助函數,你可以用它來確保頁面在執行測試之前已完全載入並準備就緒。例如:
import type { TestRunnerConfig } from '@storybook/test-runner';
import { waitForPageReady } from '@storybook/test-runner';
import { toMatchImageSnapshot } from 'jest-image-snapshot';
const customSnapshotsDir = `${process.cwd()}/__snapshots__`;
const config: TestRunnerConfig = {
setup() {
expect.extend({ toMatchImageSnapshot });
},
async postVisit(page, context) {
// Awaits for the page to be loaded and available including assets (e.g., fonts)
await waitForPageReady(page);
// Generates a snapshot file based on the story identifier
const image = await page.screenshot();
expect(image).toMatchImageSnapshot({
customSnapshotsDir,
customSnapshotIdentifier: context.id,
});
},
};
export default config;
Index.json 模式
在測試本機 Storybook 時,測試工具會將你的 story 檔案轉換為測試。對於遠端 Storybook,它會使用 Storybook 的 index.json(以前的 stories.json
)檔案(所有 story 的靜態索引)來執行測試。
為什麼?
假設你遇到本機和遠端 Storybook 似乎不同步的情況,或者你甚至可能無法存取程式碼。在這種情況下,index.json
檔案保證是你正在測試的已部署 Storybook 的最準確表示。若要使用此功能測試本機 Storybook,請使用 --index-json
標誌,如下所示:
npm run test-storybook -- --index-json
index.json
模式與監看模式不相容。
如果你需要停用它,請使用 --no-index-json
標誌
npm run test-storybook -- --no-index-json
如何檢查我的 Storybook 是否有 index.json
檔案?
Index.json 模式需要一個 index.json
檔案。開啟瀏覽器視窗並導覽至您部署的 Storybook 實例(例如,https://your-storybook-url-here.com/index.json
)。您應該會看到一個以 "v": 3
鍵開頭的 JSON 檔案,緊接著另一個名為 "stories" 的鍵,其中包含故事 ID 到 JSON 物件的映射。如果是這樣,您的 Storybook 支援 index.json 模式。
疑難排解
測試執行器似乎不穩定,且持續逾時
如果您的測試逾時並顯示以下訊息
Timeout - Async callback was not invoked within the 15000 ms timeout specified by jest.setTimeout
可能是 Playwright 無法處理您專案中大量的測試故事。也許您有大量的故事,或者您的 CI 環境的 RAM 配置非常低。在這種情況下,您應該通過調整您的命令,來限制並行執行的工作人員數量,如下所示
{
"scripts": {
"test-storybook:ci": "yarn test-storybook --maxWorkers=2"
}
}
CLI 中的錯誤輸出太短
預設情況下,測試執行器會在 1000 個字元處截斷錯誤輸出,您可以直接在瀏覽器中的 Storybook 中查看完整輸出。但是,如果您想更改該限制,可以通過將 DEBUG_PRINT_LIMIT
環境變數設定為您選擇的數字來實現,例如,DEBUG_PRINT_LIMIT=5000 yarn test-storybook
。
在其他 CI 環境中運行測試執行器
由於測試執行器基於 Playwright,您可能需要使用特定的 Docker 映像或其他配置,具體取決於您的 CI 設定。在這種情況下,您可以參考 Playwright CI 文件以獲取更多資訊。
按標籤過濾的測試執行不正確
如果您已啟用使用標籤過濾測試,並且為 include
和 exclude
列表提供了相似的標籤,則測試執行器將根據 exclude
列表執行測試,並忽略 include
列表。為了避免這種情況,請確保提供給 include
和 exclude
列表的標籤不同。
測試執行器不支援開箱即用的 Yarn PnP
如果您在啟用了 Plug'n'Play (PnP) 的較新版本 Yarn 上運行的專案中啟用了測試執行器,則測試執行器可能無法按預期工作,並且在運行測試時可能會產生以下錯誤
PlaywrightError: jest-playwright-preset: Cannot find playwright package to use chromium
這是由於測試執行器使用了社群維護的套件 jest-playwright-preset,該套件仍需要支援此功能。為了解決此問題,您可以將 nodeLinker
設定切換為 node-modules
,或者在您的專案中直接安裝 Playwright 作為直接依賴項,然後通過 install
命令新增瀏覽器二進制檔案。
了解其他 UI 測試