accelint-ts-testing について
保守しやすい Vitest テストを書くための詳細な指針を提供するスキルです。テスト構成、分離、アサーション、テストダブル、非同期処理、プロパティベーステストを扱います。
主な機能
- 01明確なテスト構成
- 02テストの独立性と分離
- 03精密なアサーション
- 04テストダブルの適切な選択
- 05非同期・プロパティベーステスト
活用シーン
- 01新しい Vitest テストスイートの設計
- 02壊れやすいアサーションのレビュー
- 03テストの分離と設定の改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gohypergiant/ agent-skills --skill accelint-ts-testingインストール後、Claude Code で/accelint-ts-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdREADME.mdassets/output-report-template.mdreferences/aaa-pattern.mdassertions.mdasync-testing.mderror-handling.mdorganization.mdparameterized-tests.mdperformance.mdproperty-based-testing.mdquick-start.mdsnapshot-testing.mdtest-doubles.mdvitest-features.md詳細
Vitest のベストプラクティス
保守しやすく効果的な Vitest テストを書くための包括的なパターンです。テストの構成、明瞭さ、パフォーマンスに関する専門的なガイダンスに焦点を当てています。
Vitest テストを書く際に絶対にしてはいけないこと
- 動作のないファイルのテストは絶対に書かないでください - 定数ファイル(
export const X = valueのみ)、型定義ファイル、GLSL の uniform 宣言、純粋なデータファイルには、テスト対象となるロジックがありません。expect(MY_CONSTANT).toBe(42)をテストしても、何も検証できません。値が変わればテストもそれに合わせて変わるため、保護にはまったくなりません。こうした「テスト」は CI の時間を無駄にし、値が変わった際のメンテナンス負担を増やします。データ宣言ではなく、動作(関数、ロジック、変換)をテストしてください。関数やロジックを含まず、型、定数、データ構造のみをエクスポートするファイルは、テストを完全に省略してください。 - グローバルなモックのクリーンアップ設定を省略しないでください - 手動でのクリーンアップは安全に見えますが、「遠隔作用」による失敗を招きます。テストファイル A のモックが数ファイル後に実行されるテストファイル B に漏れ、テストの実行順序によってのみ発生する非決定的な失敗の原因になります。こうしたハイゼンバグのデバッグで、CI に何時間も費やすことになります。
vitest.config.tsでclearMocks: true、mockReset: true、restoreMocks: trueを一度設定すれば、この種の実行順序に依存する失敗をすべて防げます。 - describe ブロックを 2 階層より深くネストしないでください - 深いネストは認知負荷と過剰なインデントを生みます。コンテキストはテスト名に入れてください。
describe('when cart is empty', () => describe('addItem', ...))ではなく、it('should add item to empty cart')とします。 - 文として読めないテストの説明は絶対に書かないでください - テストの説明は、小文字で「it ...」に続けて読んだときに文が完成する必要があります。
it('Add item to cart')やit('It should add item to cart')ではなく、it('should add item to cart')と書いてください。「it」を前に付けると、「it should add item to cart」という文として読めます。大文字で始まる説明、it('addToCart test')のような文ではない形式、冗長な「It should」は、読みやすさとテスト出力の一貫性を損ないます。例ベースのテストではit('should...')を使い、プロパティベースのテストではit('property: ...')形式を使います。 - ライブラリがすでにテストしている内部処理をテストしないでください -
expect(array.map(fn)).toEqual(expected)をテストしても、Array.prototype.map が正しく動作することを確認するだけで、時間の無駄です。JavaScript/TypeScript の標準ライブラリや、広く使われているサードパーティライブラリは、すでに十分にテストされています。lodash、React、または言語そのものが動作することを証明するのではなく、ビジネスロジックに焦点を当ててください。「このライブラリ関数は仕様どおりに動作するか」をテストしていることに気づいたら、テストする層を間違えています。ライブラリ自体の動作ではなく、コードがライブラリをどのように使うかをテストしてください。 - テストのためだけに内部関数をエクスポートしないでください - テストでは実装の詳細に踏み込まず、パブリック API を通じて動作を検証してください。プライベートヘルパー、内部ユーティリティ、実装関数をテストのためだけにエクスポートするのは、次のいずれかを示すコードの臭いです。(1)その動作をテストするにはパブリック API が不十分、または(2)テストが動作ではなく実装の詳細を検証している。内部ロジックが専用のテストを必要とするほど複雑なら、独自のパブリック API とテストファイルを持つ別のモジュールに切り出してください。プライベート関数は、それを呼び出すパブリック関数を通じて間接的にテストします。
- 独自の純粋関数をモックしないでください - 内部コードをモックすると、テストが壊れやすくなり、価値も下がります。モックするのは外部依存関係(API、データベース、サードパーティライブラリ)のみです。フェイクを優先し、次にスタブ、スパイ、モックの順に使ってください。
toBeTruthy()やtoBeDefined()のような曖昧なアサーションは絶対に使わないでください - こうしたアサーションは、意図していない複数の異なる値に対してパスします。toBeTruthy()は1、"false"、[]、{}にもパスしますが、これらは意味が異なります。リファクタリングでgetUser()の戻り値が{id: 1}から1に変わっても、テストはパスしたままなのに、本番コードは壊れます。曖昧なアサーションは誤った安心感を生み、それは本番環境で消え去ります。toBeTypeOf()は曖昧なアサーションではありません。- 動作ではなく実装の詳細をテストしないでください - 「関数 X が 3 回呼び出された」ことを検証するテストは、誤った失敗を引き起こします。メモ化によって X の呼び出しを 1 回に最適化すると、ユーザー体験は同じ(しかも高速)なのに、すべてのテストが失敗します。こうしたテストは、パフォーマンス改善やリファクタリングを積極的に妨げます。コードが内部でどのように実現するかではなく、ユーザーが目にするもの(入力に対する出力)をテストしてください。
- テスト間で可変状態を共有しないでください - 実行順序や前のテストの状態に依存するテストは、不安定で信頼できないテストスイートを生みます。各テストは、新しいセットアップを使って完全に独立させてください。
- テストファイルで
anyを使ったり、型チェックを省略したりしないでください - 実装のシグネチャが変わったとき、as anyを使ったテストは、誤った引数で関数を呼び出していても気づかれずにパスします。TypeScript が検出できたはずの壊れたコードをリリースすることになります。テストは実行可能なドキュメントです。user as anyでは何も伝わりませんが、createTestUser(Partial<User>)なら、このテストケースで重要なプロパティが明確に示されます。 - TypeScript の型チェックを実行せずにテストファイルを完了扱いにしないでください - テストファイルは通常、
tsconfig.jsonのコンパイル対象から除外されているため、プロジェクトのルートでtscを実行しても、テストの型エラーは検出されません。テストの型エラーは、実行時エラーや誤ったテスト動作を引き起こし、意図した検証ができていないテストに誤った安心感を与えます。テストファイルを「完了」とする前に、必ずプロジェクトのパッケージマネージャー(npm/pnpm/bun/yarn)を使い、そのテストファイルを指定してtsc --noEmitを直接実行してください。モノレポの場合は、まず該当するパッケージのディレクトリにcdしてから型チェックを実行します。先に進む前にすべての型エラーを修正してください。エラーを回避するためにas anyや@ts-ignoreを使ってはいけません。 - TypeScript の型が実行時エラーを防ぐと考えないでください - TS の型はコンパイル時にのみ有効で、実行時には消失します。「型に適合した」入力だけをテストすると、誤った安心感を招きます。本番環境では、検証されていない JSON API、
JSON.parse()の結果、外部ライブラリ、ユーザー入力、データベースレコードから、関数に無効なデータが渡されます。process(data: ValidData)と型付けされた関数でも、実行時にはnull、undefined、形式不正のオブジェクトを受け取る可能性があります。防御的プログラミングのシナリオをテストしてください。null 許容ではないパラメーターにnullを渡し、必須フィールドにundefinedを渡し、型付けされたパラメーターに形式不正のオブジェクトを渡します。こうした「型に適合しない」テストは、TypeScript では防げない実際のバグを検出します。 - より強いプロパティがある場合に、弱いプロパティをテストしないでください - 「例外が発生しない」や「値を返す」ことだけを検証するプロパティベーステストでは、カバレッジがほとんど得られません。エンコード/デコードの組み合わせをテストする場合は、デコードが成功することだけでなく、ラウンドトリップ時の等価性(
decode(encode(x)) === x)を検証してください。正規化をテストする場合は、文字列が返されることだけでなく、冪等性(normalize(normalize(x)) === normalize(x))を検証してください。弱いプロパティは誤った安心感を生みます。テストはパスしても、正しさを実際には検証していません。
テストを書く前に確認すること
テストを実装する前に、以下の専門的な思考パターンを適用してください。
このファイルはテストする必要がありますか?
- このファイルにはテストすべき振る舞いがありますか? ロジックを含まず、定数、型、データ構造を宣言するだけのファイルは、テストする必要がありません。定数ファイル(
export const X = 42)、型定義ファイル(type User = {...})、GLSL の uniform 宣言、設定オブジェクト、純粋なデータファイルには、検証すべき振る舞いがありません。関数もロジックも変換処理も含まないファイルは、テストを省略してください。データではなく、振る舞いをテストしてください。
テストの分離とセットアップ
- クリーンアップ処理はどこに置くべきですか? レイヤーごとに考えてください。設定ではエラーのクラス全体をなくし(
vitest.config.tsでモックをクリーンアップするなど)、セットアップファイルではプロジェクト全体に関わる処理(カスタムマッチャー、グローバルモックなど)を扱い、beforeEachではテスト固有の状態を扱います。各テストで個別にモックをクリーンアップするのは、各関数で個別に null チェックを行うようなものです。動作はしますが、本質を見落としています。適切な範囲の中で、できるだけ上位のレイヤーに処理を寄せてください。 - このテストは以前のテストや共有状態に依存していますか? テストスイートは並行して存在する別々の世界です。各テストは、最初に実行されても、最後に実行されても、単独で実行されても、同じように動作する必要があります。状態への依存は、実行順序によって成功したり失敗したりする「量子的なテスト」を生みます。あるテストのデータが必要なら、それらは実際には不自然に分割された一つのテストです。
何をテストするか
- 振る舞いと実装のどちらをテストしていますか? コードがどのように目的を達成したか(どの関数が呼び出されたか)ではなく、ユーザーが体験する内容(入力 → 出力)をテストしてください。実装をテストすると、安全なリファクタリングでもテストが壊れます。
- 最もシンプルな依存関係は何ですか? 実際の実装 > フェイク > スタブ > スパイ > モック。この階層を一段下がるごとに、壊れやすさが増します。実際のコードを使うのが現実的でない場合(外部 API、時間のかかる処理など)にのみ、モックを使ってください。
テストの明確さ
- このテストは 5 秒で理解できますか? 明確な区切りを設けた AAA パターン(準備、実行、検証)に従ってください。セットアップが複雑な場合は、説明的な名前を付けたヘルパー関数に切り出してください。
- 同じ振る舞いに複数のバリエーションがありますか? テスト構造をコピーするのではなく、パラメーター化テストには
it.each()を使ってください。概念ごとにアサーションを一つにすると、テストに集中しやすくなります。
パフォーマンスと保守性
- このテストは 6 か月後も価値がありますか? テストフレームワークの内部実装や、些細な処理のテストは避けてください。実際にバグを防ぐビジネスロジック、エッジケース、エラー処理に集中してください。
- このテストは保存するたびに実行できるほど高速ですか? テストで時間のかかる処理を避けてください。データベースにはフェイクを使い、遅延にはタイマーをモックし、外部呼び出しにはスタブを使ってください。テストは数ミリ秒で完了するべきです。
このスキルの対象範囲
vitest のテストパターンに関する専門的なガイダンス:
- 構成 - ファイルの配置、命名、describe ブロックの構造
- AAA パターン - すぐに理解できる準備、実行、検証
- パラメーター化テスト - 重複を減らすための
it.each()の使用 - エラー処理 - 例外、エッジケース、障害注入のテスト
- アサーション - 意図しない値を検出する厳密なアサーション
- テストダブル - フェイク、スタブ、モック、スパイの階層と、それぞれを使う場面
- 非同期テスト - Promise、async/await、タイマー、並行テスト
- パフォーマンス - 効率的なセットアップとグローバル設定による高速なテスト
- Vitest の機能 - カバレッジ、ウォッチモード、セットアップファイル、設定の検出
- スナップショットテスト - 保守性に役立つ場合と損なう場合
- プロパティベーステスト - fast-check と生成された入力を使った、より強力なカバレッジ
使用方法
このスキルでは、コンテキストの使用量を最小限に抑えるため、段階的開示の構造を採用しています。
1. 概要から始める(AGENTS.md)
AGENTS.md を読み、すべてのルールを簡潔にまとめた概要と、既存のテスト設定を見つけるためのワークフローを確認してください。
2. 既存のテスト設定を確認する
テストを作成する前に、以下を確認してください。
- まず
vitest.config.tsで、グローバルなモックのクリーンアップ設定(clearMocks、mockReset、restoreMocks)を確認します。 - 次に、セットアップファイル(
test/setup.ts、vitest.setup.tsなど)を探し、その設定を分析します。 - AGENTS.md のワークフローを参照してください。
3. 必要に応じて個別のルールを読み込む
以下の明確な条件に応じて、各リファレンスファイルを読み込んでください。
必ず読み込む(ファイル全体を読み込む):
- Promise/タイマーを使った非同期テストを作成する → async-testing.md
- モック、スタブ、スパイ、フェイクを扱う → test-doubles.md
- 既存のテストファイルを監査/レビューする → property-based-testing.md(PBT の適用機会を特定するため)
以下のパターンが見られる場合に読み込む:
- ネストされた describe ブロックが 2 階層を超える → organization.md
- テストファイルが実装と同じ場所にない → organization.md
- Arrange/Act/Assert の構成が明確でないテスト → aaa-pattern.md
- わずかに異なるテストコードが重複している → parameterized-tests.md
- エラーケースのテストがない、またはエッジケースのカバレッジが不十分 → error-handling.md
toBeTruthy()やtoBeDefined()など、厳密でないアサーション → assertions.md- テストの実行が遅い(テストあたり >100ms) → performance.md
- カバレッジ、ウォッチモード、または Vitest 固有の機能が必要 → vitest-features.md
- スナップショットテストを検討またはレビューする → snapshot-testing.md
- エンコード/デコードのペア、バリデーター、ノーマライザー、または純粋関数 → property-based-testing.md
- 不変条件、数学的性質、またはデータ変換を含むコード → property-based-testing.md
- 既存の fast-check またはプロパティベーステスト → property-based-testing.md
特に必要な場合を除き、読み込まないでください:
- テストが高速(<50ms)の場合、performance.md を読み込まないでください。
- スナップショットについて言及されていない場合、snapshot-testing.md を読み込まないでください。
- 基本的なテスト作成では、vitest-features.md を読み込まないでください。
4. パターンを適用する
各リファレンスファイルには、以下が含まれています。
- ❌ アンチパターンを示す誤った例
- ✅ 最適な実装を示す正しい例
- そのパターンが重要である理由の説明
5. レポートテンプレートを使用する
このスキルをテストコードレビューで呼び出す場合は、標準化されたレポート形式を使用します。
テンプレート: assets/output-report-template.md
レポート形式には、以下の項目が含まれます。
- テスト品質への影響評価を含むエグゼクティブサマリー
- 優先順位付けのための重大度レベル(Critical、High、Medium、Low)
- 影響分析(テストの信頼性、保守性、パフォーマンス、明確さ)
- 分類(テスト構成、アサーション、テストダブル、非同期テスト、パフォーマンス)
- references/ 内の詳細なガイダンスにリンクするパターン参照
- すべての問題を追跡するためのサマリーテーブル
レポートテンプレートを使用する場合:
/accelint-ts-testing <path>を介してスキルを直接呼び出した場合- ユーザーが「テストコードをレビュー」または「テストを監査」するよう求め、ファイルに対してスキルを暗黙的に呼び出した場合
レポートテンプレートを使用しない場合:
- ユーザーが「この関数のテストを書いて」と求めた場合(直接実装)
- ユーザーが「このテストの何が問題なの?」と尋ねた場合(質問に回答)
- ユーザーが特定のテスト修正を依頼した場合(正式なレポートを作成せず、修正を直接適用)
重要:テストを監査する際は、必ずプロパティベーステストの機会を確認してください
- 監査のたびに property-based-testing.md を読み込む
- AGENTS.md の「Workflow: Test Code Review/Audit」に従う
- 価値の高い PBT パターンを確認する:エンコード/デコードのペア、正規化関数、バリデーター、純粋関数、ソート関数
- 他に問題が見つからない場合でも、監査レポートに PBT の機会を含める
簡単な例
このスキルによって、不明瞭なテストがどのように明確で保守しやすいテストに変わるかを示す、変更前/変更後の完全な例については、quick-start.md を参照してください。