文件
Storybook 文件

測試附加元件

(⚠️ 實驗性)

雖然此附加元件為實驗性,但會以 @storybook/experimental-addon-test 套件發佈,且 API 可能在未來版本中變更。我們歡迎您的意見和貢獻,以協助改善此功能。

Storybook 的測試附加元件可讓您直接在 Storybook 中測試元件。它會透過使用 Vitest 外掛程式將您的stories轉換成 Vitest 測試,並使用可攜式 stories來完成。

測試 stories 的方式有兩種:冒煙測試 (Smoke test) 確保其呈現,如果定義了play 函式,則會執行該函式,並驗證其中任何做出的判斷

如果測試失敗,則會在側邊欄中標示出來,您可以按一下 story 來查看失敗狀況。您也可以在監看模式中執行測試,當您變更元件或 stories 時,系統會自動重新執行測試。

安裝和設定

在安裝之前,請確保您的專案符合下列需求

如果您尚未在使用 Storybook 8.4,則可以將您的 Storybook 升級至預發行版本

npx storybook@next upgrade

自動設定

執行下列命令來安裝和設定附加元件,其中包含使用 Vitest 將您的 stories 做為測試執行的外掛程式

npx storybook add @storybook/experimental-addon-test

add 命令會安裝並註冊測試附加元件。它也會檢查專案的 Vite 和 Vitest 設定,並視需要以合理的預設值安裝和設定它們。您可能需要調整設定以符合專案的需求。完整的設定選項可以在下方的API 區段中找到。

手動設定

對於某些專案設定,add 命令可能無法自動化附加元件和外掛程式設定,並要求您完成其他設定步驟。以下是您需要執行的動作

  1. 確保您的專案中已設定 Vite 和 Vitest。
  2. 設定 Vitest 以使用瀏覽器模式
  3. 在您的專案中安裝附加元件 @storybook/experimental-addon-test,並在您的 Storybook 設定中註冊它
  4. 建立測試設定檔 .storybook/vitest.setup.ts。您可以使用範例設定檔做為參考。
  5. 調整您的 Vitest 設定以包含外掛程式並參考設定檔。您可以使用範例設定檔做為參考。

框架外掛程式

某些 Storybook 框架需要額外的設定,才能讓框架的功能與 Vitest 搭配運作。每個框架都會匯出一個 Vite 外掛程式,您可以使用它來正確設定專案

如果您使用的是 Next.js,請先安裝 @storybook/experimental-nextjs-vite 套件

npm install --save-dev @storybook/experimental-nextjs-vite

然後套用 @storybook/experimental-nextjs-vite/vite-plugin 的外掛程式

vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
 
import viteConfig from './vite.config';
 
export default mergeConfig(
  viteConfig,
  defineConfig({
    plugins: [
      storybookTest(),
      storybookNextJsPlugin(), // 👈 Apply the framework plugin here
    ],
    // ...
  })
);

上述範例會在 Vitest 設定檔中使用框架的外掛程式。如果您的專案是如此設定的,您也可以在 Vitest 工作區檔案中使用它。

範例設定檔

當附加元件自動設定時,它會為您建立或調整 Vitest 設定檔。如果您是手動設定,您可以將以下範例作為配置專案時的參考。

Vitest 設定檔範例

Storybook 的 stories 包含定義在 .storybook/preview.js|ts 中的設定。為了確保設定在您的測試中可用,您可以在 Vitest 設定檔中應用它。以下是如何執行此操作的範例

.storybook/vitest.setup.ts
import { beforeAll } from 'vitest';
// 👇 If you're using Next.js, import from @storybook/nextjs
//   If you're using Next.js with Vite, import from @storybook/experimental-nextjs-vite
import { setProjectAnnotations } from '@storybook/react';
import * as previewAnnotations from './preview';
 
const annotations = setProjectAnnotations([previewAnnotations]);
 
// Run Storybook's beforeAll hook
beforeAll(annotations.beforeAll);

setProjectAnnotations 函數是 portable stories API 的一部分,Vitest 外掛程式在內部使用它來將您的 stories 轉換為測試。

Vitest 設定檔範例

外掛程式最簡單的應用方式是將其包含在您的 Vitest 設定檔中

vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
// 👇 If you're using Next.js, apply this framework plugin as well
// import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
 
import viteConfig from './vite.config';
 
export default mergeConfig(
  viteConfig,
  defineConfig({
    plugins: [
      storybookTest({
        // This should match your package.json script to run Storybook
        // The --ci flag will skip prompts and not open a browser
        storybookScript: 'yarn storybook --ci',
      }),
      // storybookNextJsPlugin(),
    ],
    test: {
      // Glob pattern to find story files
      include: ['src/**/*.stories.?(m)[jt]s?(x)'],
      // Enable browser mode
      browser: {
        enabled: true,
        name: 'chromium',
        // Make sure to install Playwright
        provider: 'playwright',
        headless: true,
      },
      // Speed up tests and better match how they run in Storybook itself
      // https://vitest.dev.org.tw/config/#isolate
      // Consider removing this if you have flaky tests
      isolate: false,
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  })
);
Vitest 工作區檔案範例

如果您正在使用 Vitest 工作區,您可以定義一個新的工作區專案

vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
// 👇 If you're using Next.js, apply this framework plugin as well
// import { storybookNextJsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
 
export default defineWorkspace([
  // This is the path to your existing Vitest config file
  './vitest.config.ts',
  {
    // This is the path to your existing Vite config file
    extends: './vite.config.ts',
    plugins: [
      storybookTest({
        // This should match your package.json script to run Storybook
        // The --ci flag will skip prompts and not open a browser
        storybookScript: 'yarn storybook --ci',
      }),
      // storybookNextJsPlugin(),
    ],
    test: {
      name: 'storybook',
      // Glob pattern to find story files
      include: ['src/**/*.stories.?(m)[jt]s?(x)'],
      // Enable browser mode
      browser: {
        enabled: true,
        name: 'chromium',
        // Make sure to install Playwright
        provider: 'playwright',
        headless: true,
      },
      // Speed up tests and better match how they run in Storybook itself
      // https://vitest.dev.org.tw/config/#isolate
      // Consider removing this if you have flaky tests
      isolate: false,
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  },
]);

用法

使用附加元件有多種執行測試的方法

Storybook UI

執行測試最簡單的方法是通過 Storybook UI。要執行元件的所有測試,請按側邊欄底部測試模組中的「執行測試」按鈕。

或者,您可以展開測試模組來個別執行特定類型的測試。對於那些具有監看模式的測試類型(在程式碼變更時會自動重新執行相關測試),您可以開啟或關閉該模式。

Screenshot of test module, expanded, showing test types and watch mode toggle

如果您已安裝 視覺測試附加元件,您將看到一個選項可以與 元件測試 一起執行視覺測試。

Screenshot of test module, expanded, showing Visual tests

執行測試後,您現在將在 stories 和元件上看到狀態指示器,顯示其通過、失敗或錯誤狀態。您可以按一下這些指示器以查看更多詳細資訊,並直接跳至「元件測試」附加元件面板中的失敗處。該面板為您的元件測試提供互動式偵錯器,讓您可以逐步執行每個模擬行為或斷言。

安裝測試附加元件後,「元件測試」附加元件面板會取代 「互動」附加元件面板。雖然測試機制不同,但附加元件面板本身的功能保持不變。

測試模組也會顯示執行的測試總數、通過的測試數以及失敗或錯誤的測試數。您可以按一下失敗次數,以篩選側邊欄,使其僅顯示那些失敗的 stories。

CLI

外掛程式會將您的 stories 轉換為真正的 Vitest 測試,因此您可以像執行專案中的任何其他 Vitest 測試一樣執行這些測試。通常,您的 package.json 中會有一個 test 指令碼來執行您的測試。

如果您還沒有 test 指令碼,您可以新增一個執行 Vitest 的指令碼

package.json
{
  "scripts": {
    "test": "vitest"
  }
}

如果您已經有一個執行 Vitest 以外的其他內容的 test 指令碼,您可以調整它以執行 Vitest(如上所示),或新增一個執行 Vitest 的新指令碼

package.json
{
  "scripts": {
    "test-storybook": "vitest"
  }
}

當您執行該指令碼時,外掛程式將會找到並執行您的基於 story 的測試以及任何其他 Vitest 測試。以下是使用 Vitest CLI 執行測試的範例(預設情況下,在 監看模式 中)

npm run test

我們建議(並且預設情況下配置)在 瀏覽器模式下使用 Playwright 的 Chromium 瀏覽器執行 Vitest。瀏覽器模式可確保您的元件在真實的瀏覽器環境中進行測試,這比 JSDom 或 HappyDom 等模擬更準確。這對於測試依賴瀏覽器 API 或功能的元件尤其重要。

偵錯

雖然外掛程式在測試時不需要執行 Storybook,但您可能仍然希望執行 Storybook 來偵錯您的測試。若要啟用此功能,請在外掛程式設定中提供 storybookScript 選項。當您在監看模式下執行 Vitest 時,外掛程式將使用此指令碼啟動 Storybook,並在測試失敗時提供 Story 的連結。這讓您可以快速跳至 Storybook 中的 Story 以偵錯問題。

您也可以在外掛程式設定中提供 storybookUrl 選項。當您未使用監看模式且測試失敗時,外掛程式將在輸出中提供使用此 URL 的 Story 連結。這在 CI 中執行測試 或其他 Storybook 尚未執行的環境中非常有用。

Screenshot of test failure in the console, showing a failure with a link to the story

編輯器擴充功能

使用外掛程式將您的 stories 轉換為 Vitest 測試,也可以讓您使用 Vitest IDE 整合來執行和偵錯測試。這讓您可以直接從您的編輯器(例如 VSCode 和 JetBrains IDE)執行測試。

此螢幕截圖顯示您如何使用 Vitest 擴充功能在 VSCode 中執行您的 Vitest 測試。Stories 會使用測試狀態進行註解,而且當測試失敗時,會提供 Story 的連結以進行 偵錯

Screenshot of test failure in VSCode, showing a failure attached to a story

在 CI 中

大多數情況下,在 CI 中執行 Storybook 測試是 透過 CLI 完成的。但是,若要讓測試輸出在測試失敗時連結到您發佈的 Storybook,您需要在外掛程式設定中提供 storybookUrl 選項

以下是使用 GitHub Actions 的範例。其他 CI 提供者的步驟類似,但語法或設定的詳細資訊可能有所不同。

當 Vercel、Netlify 等服務的動作執行部署工作時,它們會遵循發出 deployment_status 事件的模式,其中包含 deployment_status.target_url 下新產生的 URL。這是已發佈 Storybook 執行個體的 URL。然後,我們使用環境變數 SB_URL 將該 URL 傳遞至外掛程式設定。最後,我們更新外掛程式設定,以在 storybookUrl 選項中使用該環境變數。

.github/workflows/test-storybook.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: '18.x'
      - name: Install dependencies
        run: yarn
      - name: Run Storybook tests
        run: yarn test-storybook
        env:
          SB_URL: '${{ github.event.deployment_status.target_url }}'
vitest.workspace.ts
export default defineWorkspace([
  // ...
  {
    // ...
    {
      plugins: [
        storybookTest({
          storybookScript: 'yarn storybook --ci',
          storybookUrl: process.env.SB_URL
        }),
      ],
    },
  },
])

設定測試

Vitest 外掛程式行為的大部分設定是在 Vitest 設定和設定檔中完成的。但是,您也可以在自己的 stories 中使用 標籤來定義設定,以控制如何測試它們。

預設情況下,外掛程式會執行所有帶有 test 標籤的 stories。您可以透過在外掛程式設定中提供 tags 選項來調整此行為。這讓您可以根據 stories 的標籤來包含、排除或跳過 stories。

在此範例中,我們將對所有 Button 元件的 stories 應用 stable 標籤,但 ExperimentalFeatureStory 除外,它將具有 experimental 標籤

Button.stories.ts
// Replace your-framework with the framework you are using (e.g., nextjs, vue3-vite)
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  // 👇 Applies to all stories in this file
  tags: ['stable'],
};
export default meta;
 
type Story = StoryObj<typeof Button>;
 
export const ExperimentalFeatureStory: Story = {
  /**
   * 👇 For this particular story, remove the inherited
   *    `stable` tag and apply the `experimental` tag
   */
  tags: ['!stable', 'experimental'],
};

若要將這些標籤連結到我們的測試行為,我們可以調整外掛程式設定以排除 experimental 標籤

vitest.workspace.ts
export default defineWorkspace([
  // ...
  {
    // ...
    {
      plugins: [
        storybookTest({
          // ...
          tags: {
            include: ['test'],
            exclude: ['experimental'],
          },
        }),
      ],
    },
  },
])

如果相同的標籤同時存在於 includeexclude 陣列中,則 exclude 行為優先。

與測試執行器的比較

由於 測試執行器 需要一個正在運行的 Storybook 實例來測試您的 stories,因为它會遍歷每個 story、執行 play 函數並監聽結果。然而,Vitest 外掛程式會使用 Vite 和可攜式 stories 將您的 stories 轉換為測試,因此它不需要運行 Storybook 來測試您的 stories。由於這種對 Vite 的依賴,此外掛程式只能與使用 Vite 的 Storybook 框架(和 Next.js)一起使用。另一方面,測試執行器可以與任何 Storybook 框架一起使用。

測試執行器只是一個 CLI 工具。它沒有用於運行測試的 UI,也沒有編輯器擴展。然而,此附加元件在 Storybook 中提供了一個 UI 來運行測試,並使您能夠使用 Vitest IDE 整合來運行和除錯測試。

此外,測試執行器會在 Jest 中將您的 stories 作為編排的測試來運行,而這種編排帶來了一些複雜性。相比之下,此外掛程式會將您的 stories 轉換為真正的測試,然後使用 Vitest 運行它們,這更簡單且更具可配置性。

最後,由於更簡單的架構和 Vitest 的使用,對於大多數專案來說,此外掛程式應該比測試執行器更快。我們將在未來進行更多的基準測試以量化這一點。

常見問題解答

如果 Vitest 本身發生錯誤會怎樣?

有時,測試可能會因為 Vitest 本身內的錯誤而失敗。當發生這種情況時,Storybook UI 中的測試模組會提醒您錯誤,您可以點擊連結以完整查看錯誤。錯誤也會記錄到主控台。

Screenshot of test module, expanded, showing Vitest error

Vitest 提供了 常見錯誤的疑難排解說明

當多個環境中出現不同的測試結果時會怎樣?

當您使用此附加元件運行測試時,它們會作為 Vitest 測試運行,並具有您在專案中設定的任何設定。依預設,它們將使用 Playwright 的 Chromium 瀏覽器以瀏覽器模式運行。有時,在附加元件中(或透過 CLI)運行時,測試會失敗,但在元件測試附加元件面板中查看時會通過(反之亦然)。發生這種情況的原因是測試在不同的環境中運行,這些環境可能具有不同的行為。

我如何在 Storybook 中除錯我的 CLI 測試?

當 CLI 中的測試失敗時,此外掛程式將嘗試提供指向 Storybook 中 story 的連結,以用於除錯目的。

如果在監視模式下運行測試時 URL 無法運作,您應該檢查兩個設定選項

  • storybookUrl:確保此 URL 正確且可存取。例如,預設值為 https://127.0.0.1:6006,這可能與您正在使用的連接埠號碼不同。
  • storybookScript:確保此指令碼正確啟動 Storybook。

如果在 CI 中運行測試時 URL 無法運作,您應該確保在運行測試之前已建置和發佈 Storybook。然後,您可以使用 storybookUrl 選項提供已發佈的 Storybook 的 URL。有關範例,請參閱在 CI 中章節。

我如何確保我的測試可以在 public 目錄中找到資源?

如果您的 stories 使用 public 目錄中的資源,並且您沒有使用預設的 public 目錄位置(public),則需要調整 Vitest 設定以包含 public 目錄。您可以透過在 Vitest 設定檔中提供 publicDir 選項 來執行此操作。

我如何應用自訂 Vite 設定?

如果您在 .storybook/main.js|ts 檔案的 viteFinal 中定義了自訂操作,則需要將這些操作轉換為 Vitest 設定。這是因為此外掛程式不使用 Storybook Vite 設定。

例如,若要在 Storybook 的 Vite 設定中重新建立別名,您需要在 Vitest 設定中套用該別名

.storybook/main.js
import { mergeConfig } from 'vite';
 
export default {
  // ...
  viteFinal: async (viteConfig) => {
    return mergeConfig(viteConfig, {
      resolve: {
        alias: {
          '@components': '/src/components',
          // ...
        },
      },
    });
  },
};

上面的範例將 Vite 設定放置在 Vitest 設定檔中。如果您的專案是這樣設定的,您也可以將其放置在 Vitest 工作區檔案中。

我如何將 Storybook 測試與其他測試隔離?

某些專案的 Vite 設定中可能包含 test 屬性。由於此外掛程式使用的 Vitest 設定會延伸該 Vite 設定,因此會合併 test 屬性。這種隔離不足可能會導致您的 Storybook 測試出現問題。

若要將 Storybook 測試與其他測試隔離,您需要將 test 屬性從您的 Vite 設定移動到 Vitest 設定。然後,此外掛程式使用的 Vitest 設定可以安全地延伸您的 Vite 設定,而不會合併 test 屬性。

為什麼我們建議使用瀏覽器模式?

Vitest 的瀏覽器模式會在真實瀏覽器中運行您的測試(預設設定中透過 Playwright 使用 Chromium)。另一種替代方法是模擬瀏覽器環境,例如 JSDom 或 HappyDom,與真實瀏覽器相比,它們的行為可能有所不同。對於 UI 元件來說,它們通常依賴於瀏覽器 API 或功能,因此在真實瀏覽器中運行測試更準確。

如需更多資訊,請參閱 Vitest 關於有效使用瀏覽器模式的指南

我如何使用 WebDriver 而不是 Playwright?

我們建議使用 Playwright 在瀏覽器中運行測試,但您可以使用 WebDriverIO 來代替。若要執行此操作,您需要在 Vitest 設定檔中調整瀏覽器提供者

如何使用 Chromium 以外的瀏覽器

我們建議使用 Chromium,因為它最有可能與大多數使用者的體驗相符。不過,您可以透過調整 Vitest 設定檔中的瀏覽器名稱來使用其他瀏覽器。請注意,Playwright 和 WebDriverIO 支援不同的瀏覽器

如何自訂測試名稱?

預設情況下,story 的匯出名稱會對應到測試名稱。若要建立更具描述性的測試說明,您可以為 story 提供 name 屬性。這可讓您包含空格、括號或其他特殊字元。

export const Story = {
  name: 'custom, descriptive name'
};

API

匯出

此附加元件具有以下匯出項目

import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'

storybookTest

類型:function

一個 Vitest 插件,可將您的 story 轉換為測試。它接受一個 選項物件進行設定。

選項

該插件使用選項物件進行設定。以下是可用的屬性

configDir

類型:string

預設值:.storybook

相對於目前工作目錄,Storybook 設定所在的目錄。

如果您的 Storybook 設定不在預設位置,您必須在此處指定位置,以便插件能夠正常運作。

storybookScript

類型:string

用於執行 Storybook 的選用腳本。如果提供,Vitest 將在監看模式下執行時使用此腳本啟動 Storybook。僅在 storybookUrl 中的 Storybook 尚未可用時執行。

storybookUrl

類型:string

預設值:https://127.0.0.1:6006

Storybook 託管的 URL。這用於內部檢查,並在失敗時提供測試輸出中 story 的連結

tags

類型

{
  include: string[];
  exclude: string[];
  skip: string[];
}

預設值

{
  include: ['test'],
  exclude: [],
  skip: [],
}

要包含、排除或跳過的標籤。這些標籤在您的 story、meta 或 preview 中定義為註解。

  • include:具有這些標籤的 story 將會進行測試
  • exclude:具有這些標籤的 story 將不會進行測試,並且不會計入測試結果中
  • skip:具有這些標籤的 story 將不會進行測試,但會計入測試結果中