webpack
イベント
該当するコンテンツが見つかりませんでした
マガジン
該当するコンテンツが見つかりませんでした
技術ブログ
LINEヤフーでは、2024年に引き続き、2025年も社内の開発者を対象としたアンケート「State of LY 2025」を実施しました(昨年度の実施レポート)。昨年はWebフロントエンド開発者のみ...
AI コーディングアシスタントに簡単なこと、例えば関数名の変更やファイルの移動を依頼すると、突然復旧作業に追われることがあります。インポートが壊れたり、参照が存在しないファイルを指したりします。5 分前にコンパイルできていたコードベースが、至る所でエラーを投げ始めます。20 秒で終わるはずのリファクタリングが、5 分間のデバッグとクリーンアップセッションに変わってしまうのです。 エージェントにとってリファクタリングが難しい理由 リファクタリングは単なる大規模な検索置換ではありません。コードベースのセマンティック構造全体にわたるグラフトラバーサル問題なのです。関数名を変更すると、変更は連鎖します。ワークスペース全体のすべての呼び出し箇所、それを参照する型定義とインターフェース、import/export 文、テスト、そして(オプションで)ドキュメントとコメント。ファイルの移動はさらに複雑な波及効果を引き起こし、すべての依存ファイルのインポートパス、バレルファイル( index.ts )と再エクスポート、 tsconfig パスやバンドラー設定に組み込まれたモジュール解決の前提、Webpack 設定のような散在する設定ファイルなどに影響します。ここに根本的なミスマッチがあります。LLM はパターンマッチングを通じてもっともらしいコードを生成することに優れていますが、リファクタリングは もっともらしさよりも精度 を要求します。これは創造的なタスクではなく、シンボルの関係、言語固有のセマンティクス、プロジェクトの依存関係グラフの正確な理解を必要とする制約充足問題なのです。「正しく見える」が、深くネストさ れたモジュールの 1 つのインポートを見逃したエージェントは、単に小さなエラーを犯しただけではありません。本番環境まで表面化しないランタイム障害を導入したのです。これが、どれほど洗練されていたとしても、テキスト生成が構造的なコード変換において信頼性に欠けるツールである理由です。 問題:エージェントが効率的ではなく、非効率に動作するとき 多くの AI エージェントがリファクタリングでつまずくのは、 構造的 な編集を テキスト 編集として扱うからです。開発者が直面し続けている失敗モードをいくつか紹介します。 依頼内容: 「このメソッドの名前を変更して」 従来の失敗: エージェントはメソッド定義を更新しましたが、プロジェクト全体の呼び出し箇所を見逃しました。プロンプトで参照を更新するよう明示的に依頼した場合でも、プロセスは遅くエラーが発生しやすいループになりました。古い名前を検索して置換するのです。このプロンプトを考えてみましょう。 expression.js の get_loose_identifier を、それが何をするかをよりよく反映するように名前変更してください。このシンボルの名前変更は 4 つのファイルに伝播し、8 つの参照と 3 つのインポートに影響します。次の図の左側(従来のアプローチ)は、専用のリファクタリングツールなしでこの操作がどのように展開されるかを示しています。最初のファイル( expression.js )でシンボルの名前を変更した後、エージェントはコードベースで get_loose_identifier を検索し、複数の LLM 呼び出しとツール呼び出しを通じて CallExpression.js と AssignmentExpression.js を更新します。努力したにもかかわらず、残りの参照を見逃しています。 Kiro の対処法: 開発者が IDE でこのタスクを手動で実行する方法を考えてみましょう。 get_loose_identifier で F2 を押し、新しい名前を入力して、Enter を押します。IDE は、コードベース全体の 8 つの参照と 3 つのインポートすべてを更新しながら、自動的に名前変更を実行します。これがまさにセマンティックリネームツールが行うことです。次の図の右側(新しいアプローチ)は、Kiro が単一のツール呼び出しで名前変更全体を適切に実行する方法を示しています。 依頼内容: 「このファイルの lint エラーを修正して」 従来の失敗: エージェントは linter の出力をテキスト編集の ToDo リストとして扱いました。1 つのファイルでシグネチャの関数名を camelCase から snake_case に変更しましたが、他のファイルで「参照が見つからない」や「インポートが見つからない」エラーを導入しました。すべての使用箇所への変更の伝播に失敗したのです。 Kiro の対処法: ユーザーが直接名前変更を依頼しなくても、エージェントがセマンティックリネームツールから恩恵を受ける例を示します。ユーザーはエージェントに「 text_helpers.py の lint エラーを修正して」と依頼します。lint エラーは、 utils/text_helpers.py 内の normalizeText と slugifyTitle を snake_case に変更する必要があることを示しています。コードベースの部分的なスナップショットを以下に示します。 これらの修正をテキスト編集として扱うエージェントは、関数定義の名前を変更し、ローカル参照を修正するかもしれませんが、他の場所のインポートや呼び出し箇所を見逃す可能性が高く、実行時に ImportError / NameError を引き起こします。セマンティックリネームツールを使用することで、Kiro は定義だけでなく、 api/routes.py と services/indexer.py のインポートと呼び出しも更新します。以下の画像の通りです。 依頼内容: 「コンポーネントを再編成して – Button.tsx を src/components/ から src/shared/ui/ に移動して」 従来の失敗: エージェントはタスクを単純なファイル操作として扱いました。ファイルの移動は成功しましたが、古い場所を指すすべてのインポート文が壊れています。エージェントはその後、検索置換操作でファイルごとにインポートを修正しようとしましたが、動的インポートを見逃しました。 import('../components/Button') 。 Kiro の対処法: Kiro がインポートパスを自動的に更新する具体的な例を示します。図は、プロジェクト構造の部分的なスナップショットと依存するコードスニペットの一部を示しています。 Button.tsx を src/components/ から src/shared/ui/ に移動した後、Kiro は移動したファイルに関連するすべてのインポート文を自動的に更新します。 主な利点: 組み込みの言語サーバーが編集を処理するため、手動の検索置換は不要です。 言語認識:TypeScript/JavaScript モジュール解決を理解します。 より安全:動作するコードを壊す可能性が低くなります。 エッジケースの処理:パスエイリアス、モノレポなどに対応します。 これは、VSCode のエクスプローラーでファイルをドラッグアンドドロップしたときに起こることとまったく同じです。セマンティックリネームツールはエージェントベースの同等機能です! Kiro エージェントのリファクタリング方法 IDE は、エージェント AI の台頭以前にこの問題をすでに解決していました。VSCode でシンボルの名前を変更するために F2 を押すと、IDE は推測しません。コードの構造を理解する言語サーバーに相談し、ワークスペース全体の編集を計算し、安全に適用します。VSCode のワークスペース編集機能により、単なるテキストパターンではなく、コードの構造を理解するプログラマブルなセマンティック検索置換が可能になります。Kiro エージェントは、LLM 推論だけでリファクタリングをシミュレートしようとはしません。代わりに、エージェントは上記と同じメカニズムを使用して、これらの実証済みの IDE 機能をプログラム的に公開する 2 つの新しいリファクタリングツールを登録します。エージェントがシンボルの名前を変更したりファイルを移動したりする必要がある場合、意図をインテリジェントに認識し、適切なリファクタリングツールを選択して呼び出します。エージェントはリファクタリングワークフローを調整し、IDE の言語サーバーが正確性の検証を支援します。 これらのエージェント登録リファクタリングツールが内部でどのように機能するかを見てみましょう。 セマンティックリネームツール:正しいリネームを実現 このツールは、VSCode のシンボル名前変更 API に直接接続します。F2 を押したときに使用するのと同じものです。 vscode.prepareRename を使用してシンボルが名前変更可能かどうかを検証し(例:キーワードではない)、 vscode.executeDocumentRenameProvider を使用してワークスペース全体で必要なすべての変更を含むワークスペース編集を生成します。TypeScript、JavaScript、TSX、JSX の場合、組み込みの VSCode 名前変更プロバイダーがすべてを処理します。Python、Go、Java などの場合、ツールはインストールされた言語拡張機能とそれらが提供する言語サーバーに依存します。 スマートリロケートツール:すべてを壊さずにファイルを移動 このツールは、VSCode のファイル移動機能を使用して、すべての参照を自動的に更新しながらファイルを再配置します。VSCode のエクスプローラーでドラッグアンドドロップするのと同等のプログラム的な操作ですが、エージェントがあなたのために実行できます。 vscode.WorkspaceEdit.renameFile と vscode.workspace.applyEdit を使用して、ツールは複数のファイルにわたる包括的な変更を生成し、影響を受けるインポートを更新します。 これが重要な理由 創造性よりも精度: リファクタリングは、コードがどのように見えるべきかを LLM に想像させる必要はありません。コードが 実際に何であるか を理解し、外科的に変更できるツールが必要なのです。 実証済みのインフラストラクチャを通じた信頼: これらは実験的な LLM 機能ではなく、開発者が日常的にすでに依存しているリファクタリングインフラストラクチャとの直接統合です。F2 を押したときに機能すれば、エージェントが実行したときにも機能します。 言語に依存しない: 重い作業は言語サーバーによって行われるため、このアプローチは技術スタックと言語全体に一般化されます。 生産性の維持: 20 秒の手動リファクタリングが、5 分間の AI 生成リカバリーミッションになるべきではありません。適切なツールを使用すれば、操作は高速でアトミックなままです。 より大きな視点 構築による正確性という私たちの哲学に基づいて、 IDE 診断統合 を導いたのと同じ原則で、VSCode のリファクタリング機能の全範囲をカバーするようにこのアプローチを拡張しています。エラーが複合する前にキャッチするためにリアルタイム診断を統合したのと同様に、これらの実証済みの決定論的 IDE 機能を、新しい内部スマートリロケートおよびセマンティックリネームツールに拡張しました。 しかし、リファクタリング機能は名前変更と再配置で止まりません。VSCode の言語サーバーは、エージェントが活用すべき豊富な自動コード変換スイートを提供します。コードブロックを再利用可能な関数に抽出するメソッド/関数の抽出、コードを簡素化する変数/関数のインライン化、すべての呼び出し箇所でメソッドパラメータを更新するシグネチャの変更、アロー関数への変換やその他の言語固有の変換は有力な候補です。 このアプローチを取ることで、エージェントが実行される基盤に正確性、セキュリティ、信頼性を組み込むことができます。これらのツールで確立したパターンは、ツールキットへの新しい追加を導きます。LLM に脆弱なテキスト置換スクリプトを生成するよう依頼する代わりに、インテリジェントなコーディングエージェントは、開発者がすでに信頼しているこれらの実証済みの IDE 操作を活用し続けます。IDE が正しく実行する方法を知っているとき、私たちはそれに作業をさせます。エージェントがより有能になるにつれて、これはその出力をより信頼できるものにするための良いテクニックでもあります。 違いを体験する準備はできましたか? Kiro を無料で始めて 、開発ワークフローをどのように変革できるかを確認してください。 Discord の成長するコミュニティに参加して、フィードバックを共有し、質問をし、AI 支援コーディングで構築している他の開発者とつながりましょう。 謝辞 エンジニアリングの洞察と貴重なフィードバックを提供してくれた Al Harris に感謝します。 本記事は 2026 年 2 月 5 日に公開された Pardis Pashakhanloo と Rajdeep Mukherjee による “ Refactoring made right: how program analysis makes AI agents safe and reliable ” を翻訳したものです。翻訳は Solutions Architect の吉村が担当いたしました。
はじめに # ビジネスソリューション事業部の塚野です。 皆さんはフロントエンド開発の際にコンポーネントのテストをどのように行っているでしょうか? 自分は最近になり、Storybook というオープンソースツールに入門しました。 https://storybook.js.org この Storybook は UI カタログを作成するサービスです。 コンポーネントをアプリ本体から切り離して単体で描画でき、Props や状態のパターンを「ストーリー」として整理ができます。 また、見た目の確認だけではなく、クリックなどのユーザーイベントを伴うコンポーネントの「ふるまい」のテストも Storybook 上で行えます。 このふるまいのテストはテストランナーに Vitest を使うことができ、 他の Vitest で作成した単体テストと一緒に一括実行が可能 です。 Storybook は様々なフロントエンドフレームワークに対応しています。その中で今回は人気のあるフレームワークとして Next.js でのコンポーネントテストの導入についてご紹介します。Storybook の2026年2月18日執筆時点での最新バージョンは v10.2.7 ですが、この構成を整理した情報はまだ多くないため、コンポーネントテストの作成だけでなくセットアップ手順も含めて具体例とともにまとめます。 書いているうちに長くなってしまったため、2回に分けました。 本記事では「Vitestと統合可能!StorybookでNext.js v16のコンポーネントテストを行う」の前編として、Storybook の導入と基本的な使い方、インタラクションテストの作成と Vitest テストランナーでの実行について記述します。 後編では Next.js 特有のビルトインパッケージのモックや、App Router での設定、モジュールモックなどについてご紹介します。 Storybookの導入と基本的な使い方 # まずは Storybook の導入です。以下のコマンドを実行します。 npm create storybook@latest 2026年2月18日執筆時点での Storybook の最新版は v10.2.7 です。Storybook では v10 以降から Next v16 に対応しています。(が、一部未対応の機能もあります。これについては後編で触れます。)Next の必須バージョンは v14 以上です。 上記のコマンド実行後、"New to Storybook?" と聞かれます。"Yes" を選んだ場合、簡単なチュートリアルとサンプルのストーリーファイルが作成されます。必要に応じて選択してください。 その後、"What configuration should we install?" と聞かれますがここは "Recommended" を選択し、オススメ設定で実行してもらいます。設定ファイルにアドオンの追加や Vitest の設定ファイルの作成などしてくれるのでこちらを選択しましょう。 ストーリー作成の前に設定ファイルをプロジェクトに合わせて変更します。 Storybook の設定ファイルはプロジェクトルートの .storybook 配下に作成されます。( Configure Storybook | Storybook docs ) Recommended 設定の場合 .storybook 配下は以下のようになっています。 / └── .storybook ├── main.ts #Storybookのメイン設定ファイル ├── preview.ts #グローバルなスタイル等の設定ファイル └── vitest.setup.ts #Storybookでのvitest設定ファイル .storybook/main.ts を以下のように変更します。 Recommended 設定の場合自動的に入っていますが、Minimum 設定の場合 "addons" に @storybook/addon-vitest と @storybook/addon-docs が追加されていることを確認してください。 main.ts import type { StorybookConfig } from '@storybook/nextjs-vite'; const config: StorybookConfig = { "stories": [ "../components/ui/**/*.stories.@(js|jsx|mjs|ts|tsx)" // ← プロジェクトに合わせて編集する ], "addons": [ "@chromatic-com/storybook", "@storybook/addon-vitest", // ← vitestとしての実行に必要 "@storybook/addon-a11y", "@storybook/addon-docs", // ← Document機能の利用に必要 "@storybook/addon-onboarding" // ← チュートリアル用のアドオン。必要ないなら削除してもOK ], "framework": "@storybook/nextjs-vite", "staticDirs": [ "../public" ] }; export default config; Storybook Config オブジェクトの "stories" 要素にストーリーファイルのパスを記述します。 ストーリーファイルは Button.stories.tsx のように .stories を付けて作成します。本記事でのデモプロジェクトでは components/ui 配下にコンポーネントファイルと共に作成します。プロジェクトに合わせて記述を変更してください。 Next.js プロジェクトでは tailwind CSS を利用している場合が多いかと思います。Storybookで tailwind CSS を有効化する場合は、 .storybook/preview.ts で globals.css を import します。 .storybook/preview.ts import type { Preview } from '@storybook/nextjs-vite' import '../app/globals.css'; // ← globals.cssをimport const preview: Preview = { parameters: { ... }, tags: ["autodocs"], // ← Document生成をすべてのStoryで有効化する }; export default preview; Storybook は各コンポーネントを Canvas と呼ばれる UI 上に表示させますが、内部では "preview" と呼ばれる iframe 内で動作させています。この preview に関する設定が preview.ts であり、ストーリーの表示に関するグローバルな設定が可能です。 後述する Document という機能が大変便利なので、ここですべてのストーリーで Document を生成する設定を追加します。Preview オブジェクトの tags 要素に ["autodocs"] を指定します。Document は各ストーリーファイル内で個別に有効化もできます。 これで準備ができました。 試しに以下のようなボタンコンポーネントを components/ui 配下に作成し、そのストーリーファイルを作って Storybook を実行してみます。 Props は size と variant を受け取り、variant でプリセットとして設定した primary と outline に見た目を切り替えられます。 ここでは tailwind-variants というライブラリを使い variant と size のプリセットを variants として定義しています。 コンポーネントのコードは軽く読み飛ばしていただいて大丈夫です。 components/ui/Button.tsx import React from "react"; import { tv, type VariantProps } from "tailwind-variants"; const buttonStyles = tv({ base: "inline-flex items-center justify-center rounded-md font-semibold transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 disabled:opacity-60 disabled:cursor-not-allowed", variants: { size: { small: "px-3 py-1.5 text-sm", medium: "px-4 py-2 text-base", large: "px-5 py-3 text-lg", }, variant: { primary: "bg-blue-600 text-white border border-blue-600 hover:bg-blue-700 focus-visible:outline-blue-500", outline: "bg-white text-slate-900 border border-slate-300 hover:bg-slate-50 focus-visible:outline-slate-400", }, }, defaultVariants: { size: "medium", variant: "primary", }, }); type ButtonVariants = VariantProps<typeof buttonStyles>; export type ButtonProps = Omit< React.ButtonHTMLAttributes<HTMLButtonElement>, "className" > & ButtonVariants; export const Button = ({ size, variant, type = "button", children, ...props }: ButtonProps) => { return ( <button type={type} className={buttonStyles({ size, variant })} {...props} > {children} </button> ); }; export default Button; このボタンコンポーネントの Story ファイルはこのように作成しました。 Button.stories.tsx import type { Meta, StoryObj } from "@storybook/nextjs-vite"; import { fn } from "storybook/test"; import { Button } from "./Button"; const meta = { title: "UI/Button", component: Button, parameters: { layout: "centered" }, argTypes: { size: { control: { type: "inline-radio" }, options: ["small", "medium", "large"], description: "ボタンのサイズ", }, variant: { control: { type: "inline-radio" }, options: ["primary", "outline"], description: "ボタンのバリアント", }, }, args: { children: "送信", size: "medium", variant: "primary", onClick: fn(), }, } satisfies Meta<typeof Button>; export default meta; type Story = StoryObj<typeof meta>; export const Default: Story = {}; export const Outline: Story = { args: { variant: "outline", size: "large", children: "キャンセル" }, }; export const Disabled: Story = { args: { disabled: true, children: "無効" }, }; コンポーネントの指定やどのような種類の Props を渡せるのかといったストーリーのメタ情報を meta オブジェクトに記載し、これを default export します。 この meta オブジェクトから Story の型を生成し、Story オブジェクトを作成、export します。 Story オブジェクトがそのままストーリーとして Storybook 上で表示されます。オブジェクト名がストーリの表示名、 args でそのストーリーでコンポーネントに渡す Props を定義できます。 npm run storybook で Storybook を実行してみましょう。 Button コンポーネントが Canvas 内に表示されました。下の「Controls」タブでは children や Props の操作ができ、その場でコンポーネントの見た目やふるまいの確認ができます。 Controls に表示される Props は args で渡したものになります。 今回 meta オブジェクトでも args を記述しており、これがデフォルトで渡される args になります。 Props を args で記述するほかに、 argTypes で Props の詳細も記述できます。 args に記載されていない Props でも argTypes へ記載した場合、 Controls タブに表示されるようになります。 また、Controls タブでの表示方法も設定でき、例えば control: { type: "inline-radio" } と記述すればユニオン型などの場合横並びのラジオボタンで値の切り替えが可能となります。(デフォルトはセレクトボックス) Document の自動生成を有効化した場合、"Docs" というタブがサイドバーに表示されます。 ここでは作成したストーリーのメタ情報やストーリーの一覧表示などが可能で、コンポーネントの概要が一目でわかるようになっています。 ButtonコンポーネントのStory DocsでPropsなどの情報も含めたDocumentが参照できる Documentでは作成した全ストーリーを一覧で表示可能 この Document にはマークダウン形式で文章も記述可能です。 以下のように特定の場所に JSDoc 形式でコメントを記述した場合 Document 内に表示されます。JSDoc 内ではマークダウン記法がサポートされています。 Button.stories.tsx ... /** * Button コンポーネントの Storybook ストーリー * * | variant | スタイル | * |---------|----------| * | primary | メインアクション用の強調されたスタイル | * | outline | 補助的なアクション用のアウトラインスタイル | */ const meta = { title: "UI/Button", component: Button, ... } ... ここまでの基本的な使い方でコンポーネントの「見た目」についての確認はできました。 Storybook ではさらに、クリック時の挙動などユーザーインタラクションを含む「ふるまい」のテストが行えます。 コンポーネントテストの導入 # 各 Story ではふるまいに関するテスト(インタラクションテスト)を "play function" として記述ができます。( Interaction tests | Storybook docs ) 先ほど作った Button コンポーネントに play function を追加して「クリックすると onClick が1度だけ呼ばれること」を確認します。 Button.stories.tsx import type { Meta, StoryObj } from "@storybook/nextjs-vite"; import { expect, fn, userEvent, within } from "storybook/test"; // ← インタラクションテストに関するパッケージから import import { Button } from "./Button"; const meta = { ... , args: { children: "送信", size: "medium", variant: "primary", onClick: fn(), // ← onClick にはスパイ関数 fn() を渡す }, } satisfies Meta<typeof Button>; export default meta; type Story = StoryObj<typeof meta>; ... /** play functions の例: ボタンをクリックすると onClick が1回呼ばれることを確認 */ export const ClickTest: Story = { args: { children: "Click Me !" }, play: async ({ canvasElement, args }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getByRole("button")); await expect(args.onClick).toHaveBeenCalledTimes(1); }, }; インタラクションテスト用の Story "ClickTest" を追加しました。 インタラクションテストは Story の "play" 要素に非同期関数として記述します。 ユーザーイベントの模倣やアサーションには storybook/test パッケージのオブジェクト、関数を利用します。 play 内では順に、 Canvasを取得 Canvas内 "button" 要素を取得 [1] 、クリック args の onClick が1回呼ばれるかをアサート をしています。userEvent と expect は必ず await の内側で呼ぶ必要があります。 args の onClick では meta オブジェクトで定義されるように fn() を渡しています。 これは Vitest のスパイ関数ですが、 storyboo/test パッケージから利用可能です。実行されると Story の Actions タブにイベントが出力されます。( Via storybook/test fn spies ) それでは、ClickTest ストーリーを表示してテスト結果を確認してみましょう。 ストーリーを表示すると自動でテストが実行されます。結果は Interactions タブから確認ができます。 無事、テストを Pass していることが確認できました。 すべてのインタラクションテストは Storybook の UI 上から一括実行が可能です。 サイドバー下部の "Run tests" をクリックで一括実行が行われます。"Interaction" にチェックがついていることを確認してください。 サイドバー内 Run tests からplay functions の一括実行が可能 Storybook の起動には高速起動が人気の Vite が利用可能です [2] 。 とはいえテストのたびに起動して UI 上で結果を確認するのも手間です。また、コンポーネントのテストも CI パイプライン上で他の単体テストと一括で実行したくなります。 そこで、Storybook ではインタラクションテストを Vitest のテストとして CLI 上で実行可能とするアドオン "Vitest addon" が提供されています。( Vitest addon | Storybook docs ) このアドオンにより .stories ファイルをヘッドレスブラウザ上で実行可能なテストに変換し、既存の Vitest と一緒に vitest コマンドで実行可能とします。 Storybook セットアップ時に "Recommended" 設定を選択した場合、Vitest に関する設定ファイル( vitest.config.ts 、 .storybook/vitest.setup.ts )が自動的に作成されます。 vitest.config.ts import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { defineConfig } from 'vitest/config'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; import { playwright } from '@vitest/browser-playwright'; const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); export default defineConfig({ test: { projects: [ { extends: true, plugins: [ // ↓ Storybookの設定ファイルを取得、main.tsに記載したパスの.storiesファイルをテスト実行対象とする storybookTest({ configDir: path.join(dirname, '.storybook') }), ], test: { name: 'storybook', browser: { enabled: true, headless: true, provider: playwright({}), instances: [{ browser: 'chromium' }], }, setupFiles: ['.storybook/vitest.setup.ts'], }, }, ], }, }); .storybook/vitest.setup.ts import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; import { setProjectAnnotations } from '@storybook/nextjs-vite'; import * as projectAnnotations from './preview'; // This is an important step to apply the right configuration when testing your stories. // More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]); vitest.config.ts では .stories を対象とするテストプロジェクト「storybook」が追加されています。 .test 、 .spec を対象とする Vitest は別のプロジェクトとして作成します。こうすることで Storybook のテストのみを対象に Vitest を実行でき、一括実行の際にはタグを分けることで Storybook のテストと関数のテストをCLI 上で区別して表示ができます。 最後に package.json へスクリプトを追加しましょう。 package.json { "scripts": { "test": "vitest", "test-storybook": "vitest --project=storybook" } } "npm run test-storybook" で Stroybook のテストのみ実行可能です。 ここは既存のテストと一括実行を考えて npm run test を実行します。 $ npm run test > storybook-demo@0.1.0 test > vitest DEV v4.0.18 /home/tsukano/storybook-demo/ 3:02:47 PM [vite] (client) Re-optimizing dependencies because lockfile has changed ✓ storybook (chromium) components/ui/Button.stories.tsx (4 tests) 501ms ✓ Default 357ms ✓ Outline 57ms ✓ Disabled 28ms ✓ Click Test 58ms Test Files 1 passed (1) Tests 4 passed (4) Start at 15:02:46 Duration 3.84s (transform 0ms, setup 1.14s, import 49ms, tests 501ms, environment 0ms) 無事 Vitest から .stories が呼ばれテストに Pass することが確認できました。 テストの実行には Playwright を使用しています。そのため Storybook のテストは関数の UT と比べ若干実行に時間がかかります。 実際の CI パイプラインへの統合についてはこちらの公式ドキュメントを参考にしてください。( Testing in CI | Storybook docs ) おわりに # 本記事では Storybook の導入と基本的な使い方、Vitest の実行についてご紹介しました。 また、基本的にローカル実行のみについて取り上げています。デプロイについては公式ドキュメントを参照してください。( Publish Storybook | Storybook docs ) 次回は Next.js 固有の設定や、ルーターオブジェクトのモック、モジュールのモックなどについてご紹介します。 ちなみに、ボタン要素の取得は getByRole() で行っています。Storybook 公式ドキュメントでは要素の取得はなるべく実際の人が目で見て行う操作に近い方法で行うべきだとしています。内部の "id" などで要素を取得するのは最終手段です。( Querying the canvas ) ↩︎ Next.js の場合、 main.ts の "framework" 要素で Vite と webpack で利用するビルドツールを選択できます。 "@storybook/nextjs-vite" を渡した場合 Vite でビルドしますが、特段の理由がない限り Vite を選択していいと思います。また、本記事の肝である Vitest も Vite を選択した場合でしか利用できません。 ↩︎
動画
該当するコンテンツが見つかりませんでした
書籍
該当するコンテンツが見つかりませんでした







