mcpField
T
test-generation

test-generation

github.com/jablonkaiテスト・品質443 回インストール今日更新
リポジトリ

test-generation について

コーディングエージェント向けのテスト生成スキルです。既存コードや修正済みバグに対し、プロジェクトの流儀に沿ったテストを作成・実行し、未検証箇所も報告します。境界値やエラー経路の選定を重視します。

主な機能

  1. 01プロジェクトのテストフレームワークに沿った生成
  2. 02境界値とエラー経路の検証
  3. 03回帰・単体・統合・UIテストへの対応
  4. 04テスト実行と未検証箇所の報告
  5. 05スタックに応じたテスト配置

活用シーン

  1. 01関数やモジュールへのテスト追加
  2. 02修正済みバグの回帰テスト作成
  3. 03カバレッジの低いモジュールで次のテストを選定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jablonkai/skills --skill test-generation

インストール後、Claude Code で/test-generationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
flutter-dart.md
kotlin-kmp.md
other-languages.md
rust-cpp.md
swift-apple.md
test-design.md

詳細

テスト生成

目的

既存のコードを、実際に不具合を検出できるテストに変換します。入力は関数、クラス、モジュール、またはバグ報告です。出力は、プロジェクトで採用されているフレームワークを使い、適切なソースセットに配置され、実行して成功するテストファイルと、未テストの項目についての簡潔な説明です。

価値があるのは、テストを書くことではなく、ケースを選ぶことです。正常系だけをたどるテストはコードの説明にはなりますが、コードを保護するものではありません。実際の不具合の多くは境界やエラー経路で発生するため、そうした箇所をケースの対象にします。

使用する場面

  • 「<file/function/class> のテストを書いて」— 新規コードにも既存コードにも対応します
  • バグを修正したばかりで、修正がなければ失敗する回帰テストが必要な場合
  • テストのないモジュールに、テスト一式を用意する場合
  • カバレッジが低く、次にテストする「量」ではなく「対象」を決めたい場合

このスキルの対象外:

  • テスト不足にも触れる、プロジェクト全体の監査 → code-analyzer
  • テストやアプリが失敗しており、根本原因を特定したい場合 → error-debugging
  • プロジェクトにまだ存在しないテストフレームワークのセットアップ → 先にセットアップしてください(スタックのリファレンスを参照)。その後、このスキルに戻ってください

先に、より具体的なスキルを使う

一部のスタックには、専用のスキルがあります。該当する場合は、ここで説明を再実装せず、専用スキルを呼び出してください。このスキルは、周辺の判断(どのケースを選ぶか、どのソースセットを使うか、カバレッジ)に使い、専用スキルがない場合に限って適用してください。

状況呼び出すスキル
package:test を使った Dart のユニットテストdart-add-unit-test
mockito と build_runner を使った Dart のモックdart-generate-test-mocks
Flutter のウィジェットテストflutter-add-widget-test
Flutter の integration_test フローflutter-add-integration-test
Compose / CMP の UI テスト、セマンティクス、スクリーンショットテストcompose-ui-testing-patterns

Swift、Rust、C++、および UI 以外の Kotlin/KMP には専用スキルがありません。このスキルで直接対応します。

前提条件

1. 使用しているスタックと、すでに採用されているフレームワークを確認します。 拡張子だけで判断せず、ビルドファイルから検出してください。KMP の commonTest ソースセットにある .kt ファイルと、Android のユニットテストにある .kt ファイルでは、テストの方法が大きく異なります。

ビルドファイル / シグナルスタックリファレンス
build.gradle.kts に kotlin("multiplatform") と commonTest があるKotlin Multiplatformkotlin-kmp.md
build.gradle(.kts) に com.android.application と androidTest/ があるKotlin/JVM、Androidkotlin-kmp.md
androidx.compose.* または compose-multiplatform の依存関係があるCompose / CMPcompose-ui-testing-patterns スキル
pubspec.yaml があるFlutter / Dartflutter-dart.md
Package.swift、*.xcodeproj、*.xcworkspace があるSwift(iOS/macOS)swift-apple.md
Cargo.toml があるRustrust-cpp.md
CMakeLists.txt、conanfile.txt、*.cpp があるC / C++rust-cpp.md
package.json、pyproject.toml、go.mod、Maven の pom.xml があるJS/TS、Python、Go、Javaother-languages.md

スタックのリファレンスは、該当するものを1つだけ読み込んでください。複数読み込むと、適用されない慣習が混在し、コンテキストを浪費します。

2. テスト一式はすでにありますか? 何かを書く前に、既存のテストファイルを2〜3個読んでください。プロジェクトの命名規則、アサーションライブラリ、フィクスチャのスタイル、モックの方法はすでに決まっています。このスキルの慣習よりも、それらに合わせることが重要です。

# find the existing suite and its conventions
find . -type d \( -name test -o -name tests -o -name '*Test*' -o -name commonTest \) -not -path '*/build/*' -not -path '*/node_modules/*' | head -20

テスト一式がまったくない場合は、その旨を伝えてください。プロジェクトで最初のテストを追加する際は、フレームワークの依存関係やテストランナーの設定が必要になることが多く、ユーザーが想定しているより大きな変更になる可能性があります。

ワークフロー

ステップ 1:テスト対象のコードを読む

シグネチャだけでなく、実際の実装を読んでください。テストでアサートできる点と、テストを難しくしている点を確認します。

  • 契約 — 入力、出力、スローまたは返却されるエラー、文書化された不変条件
  • 分岐 — すべての if、when/switch、早期リターン、ループ境界、?: のフォールバック
  • 依存関係 — 置き換えが必要なもの(時計、ネットワーク、ファイルシステム、データベース、乱数)
  • 状態 — 結果は入力だけで決まる純粋関数か、それとも過去の呼び出しに依存するか
  • 並行処理 — サスペンド関数、async/await、スレッド、アクター、チャネル

Serena などのシンボルを認識するツールを使い、ファイル全体ではなく、対象のシンボルとその呼び出し箇所を読みます。呼び出し箇所を見るとコードが実際にどう使われているかがわかり、現実的なケースを導き出せます。

コードが現状のままではテストできない場合(ハードコードされたシングルトン、隠れた時計、I/O を行うコンストラクターなど)は、そのことを伝え、解決に必要な最小限の変更を示してください。テストを可能にするために、本番コードを黙ってリファクタリングしないでください。提案にとどめるかどうかは、ユーザーに判断してもらいます。

ステップ 2:ケースを選ぶ

テンプレートではなく、契約と分岐からケースを導き出します。テスト対象の各単位について、次のカテゴリを検討し、このコードに実際に当てはまるものを選びます。

カテゴリ確認すること
正常系典型的な値を使った一般的な呼び出し — メソッドごとではなく、意味のある動作ごとに 1 つ
境界値0、1、多数、最初、最後、最小、最大、すべてのインデックスと長さの境界を 1 つ超える値
空/不在空文字列、空のコレクション、null/None/nil、省略されたオプション値、デフォルト引数
無効な入力不正なデータ、範囲外の値、言語が許す場合は型の不一致
エラー経路スローされるすべての例外、Err、失敗した Future と、それらからの復旧処理
状態遷移2 回呼び出す、順序を変えて呼び出す、close/dispose の後に呼び出す
並行処理キャンセル、タイムアウト、呼び出しの競合 — 実際に並行処理が行われる場合に限る

ケースリストの大半を決めるルールは 2 つです。

  • 1 テストにつき 1 つの動作。 5 つのことをアサートするテストは、1 つのメッセージで失敗し、残り 4 つについては何もわかりません。
  • 失敗する可能性があるケースだけを選ぶ。 想定できる実装変更をしてもテストが失敗しないなら、そのテストは何も検証していないため、削除します。

バグ修正のリグレッションテストでは、順序を逆にします。まずバグを再現するテストを書き、修正前のコードで失敗することを確認します(それが不可能な場合は、その理由を説明します)。その後、そのテストを残します。

ケース選択のヒューリスティック、命名、フィクスチャ/モック/フェイクの選び方については、test-design.md を参照してください。契約からケースが明らかでない場合に読み込みます。

ステップ 3:ファイルを正しい場所に配置する

配置が間違っていることが、生成されたテストが実行されない最も一般的な原因です。スタックのリファレンスに正確なルールが記載されています。基本的な構成は常に同じです。

  • テストのソースルートでソースパスを再現し、テスト対象の単位にちなんだファイル名を付ける
  • テストに必要なものに応じてソースセットを選ぶ。共有ロジックや純粋なロジックは common または plain の単体テストセットに配置し、デバイス、実際のフレームワーク、UI が必要なものは platform/instrumented のセットに配置する
  • 既存のテストスイートのパッケージ/モジュール宣言に合わせる
ステップ 4:テストを書く

ステップ「Prerequisites 2」で確認した規約と、スタックリファレンスに記載されたイディオムに従います。

  • Arrange、act、assert — この順に、目で見てわかるように分けます
  • 説明的な名前 — 条件と期待する結果を名前に含めます。ファイルを開かなくても失敗内容がわかります
  • 実装ではなく動作をアサートする — 内部呼び出しをアサートすると設計が固定され、リファクタリングのたびに壊れます
  • 必要なものだけを置き換える — フェイクより実オブジェクト、モックよりフェイクを使います。自分が所有する型をモックするのは、通常、設計上の問題を示しています
  • 構造上、決定的になるようにする — クロックを注入し、乱数ソースにシードを設定し、ディスパッチャー/スケジューラーを制御します。非同期処理を待つために sleep を使ってはいけません
  • 重複よりフィクスチャを優先する — ただし、誰も把握できないフィクスチャ階層を作るより、テストで多少重複するほうが低コストです

プロジェクトの既存テストスイートに自然に馴染むテストを書きます。

ステップ 5:テストを実行する

実行していない生成テストは、まだ下書きです。テストスイートを実行します。まず新しいテストに絞り、その後、ファイル全体を実行し、失敗したものを修正します。

正確なコマンドはスタックリファレンスに記載されています。出力は失敗箇所に絞り、実行結果全体を貼り付けないようにします。

次に、テストに価値があることを確認します。テストが失敗するはずの方法で実装を変更し(頭の中で試すか、実際に変更して元に戻します)、該当するテストで検出できることを確認します。意図的に壊した実装でもテストが通る場合は、正直に報告します。つまり、そのテストは何もアサートしていません。

ステップ 6:カバレッジと未対応箇所を報告する

プロジェクトでカバレッジツールが設定されている場合は実行します(Kover、flutter test --coverage、xccov、cargo llvm-cov、gcov/llvm-cov、または JS/Python の同等ツール。スタックリファレンスを参照)。設定されていない場合、依頼されていないツールの追加は行わず、分岐を読んで未対応箇所を報告します。

報告するのは割合ではなく、未対応箇所です。

## Tests added
<file paths, and one line per behavior covered>

## Verification
<command run, and the real result>

## Still untested
<specific branches or paths, as `file:line` — with why, if there is a reason>

カバレッジは未テストの分岐を見つけるためのもので、目標値ではありません。数値を上げることだけを目的にテストを追加してはいけません。

操作

操作ユーザーの意図出力
unit(デフォルト)「この関数/クラスのテストを書いて」依存関係を置き換えた分離テスト。実行して成功済み
integration「これをエンドツーエンドでテストして」「モジュールをまとめてテストして」実際の連携先(DB、HTTP、ファイルシステム)を使ったテスト。セットアップ/ティアダウンを含む
ui「この画面/ウィジェット/ビューをテストして」スタックの UI ハーネスによる UI テスト。専用スキルがある場合は、そこに引き継ぎます
regression「修正したばかりのバグのテストを書いて」修正がなければ失敗する、焦点を絞ったテストを 1 つ
coverage「何がテストされていないか教えて」カバレッジの実行と、未テストの分岐を優先度順に並べた一覧。依頼された場合に限りテストを追加

重要な制約

  • 作り上げた動作をアサートしてはいけません。 実装を読みます。意図した動作が本当に曖昧な場合は、アサーションとして推測を組み込まず、質問します。
  • 実行していないテストを書いてはいけません。 失敗を隠さず報告します。
  • テストを通すために本番コードを変更してはいけません。 それでは本来の目的が逆転します。コードがテスト不可能な場合は、別途変更を提案します。
  • テストを追加する際、既存のテストスイートを書き換えたり「改善」したりしてはいけません。
  • 自分が書いていない失敗テストを削除したり、弱めたりしてはいけません。 赤いテストは情報です。
  • ユニットテストでは、スリープ、実時間への依存、ネットワーク利用を禁止します。 不安定なテストは、赤を無視する習慣を生むため、テストがないよりも悪いものです。
  • カバレッジの数値を追い求めてはいけません。 アサーションのないテストやゲッターのテストはカバレッジを引き上げるだけで、何も守りません。
  • 実際の認証情報、トークン、本番データをフィクスチャに含めてはいけません。 テストデータはコミットされ、公開されます。

参考資料

  • test-design.md — 横断的なテストケースの選定、命名、フィクスチャとフェイクとモックの違い、カバレッジレポートの読み方
  • kotlin-kmp.md — kotlin.test と JUnit、ソースセット、runTest/TestDispatcher、Turbine、MockK、Kover
  • flutter-dart.md — 専用の Dart/Flutter スキルへの案内、ゴールデンテストとカバレッジ
  • swift-apple.md — Swift Testing、XCTest、XCUITest、非同期テスト、xccov
  • rust-cpp.md — cargo test、ドキュメントテスト、proptest、criterion、llvm-cov、GoogleTest、Catch2、CTest
  • other-languages.md — Jest/Vitest、pytest、Go の testing、標準の JUnit

コメント