dart-test-fundamentals について
dart-test-fundamentalsは、Dart開発者向けにテストの構成と設定を案内するスキルです。テスト構造、ライフサイクル、後処理を扱い、規約を適用しない場面も示します。
主な機能
- 01testとgroupによるテスト構成
- 02setUpとtearDownのライフサイクル適用
- 03プラットフォーム、タグ、タイムアウトの設定
- 04リソース後処理とテストダブルの利用
- 05アサーションとテストシームの指針
活用シーン
- 01Dartテストスイートの作成や整理
- 02dart_test.yamlでのテスト実行設定
- 03テストリソースの後処理方法の選択
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ kevmoo/ dash_skills --skill dart-test-fundamentalsインストール後、Claude Code で/dart-test-fundamentalsを入力して読み込まれたか確認します。
同梱ファイルevals/evals.json
詳細
Dart テストの基礎
このスキルを使う場面
次のような場合にこのスキルを使います。
- 新しいテストファイルを作成するとき。
groupを使ってテストスイートを構成するとき。dart_test.yamlでテストの実行を設定するとき。- テストのライフサイクルメソッドを理解するとき。
使わない場面(適用を控える基準)
次のような場合は、このスキルを適用したり、既存のテストをリファクタリングしたりしないでください。
- 単一グループを使ったレガシーコードの変更:明示的に依頼されない限り、手を加えていない既存のテストから
groupの階層を削除したり、書式を変更したりしないでください。差分が不必要に増える原因になります。 - 別のアサーションフレームワーク:パッケージが
package:checksまたは専用のテストフレームワークに移行済みの場合、テストをレガシーなpackage:matcherのイディオムに戻さないでください。 - セットアップ不要の簡単なテスト:共有状態やリソースのない単純な独立テストには、
group、setUp、addTearDownは不要です。形式だけのラッパーを追加しないでください。
検出方法
テスト構成の改善候補を見つけるには、次の方法を使います。
try-finally によるクリーンアップ
addTearDown の代わりに try-finally でクリーンアップしているテストを検索します。
- 正規表現:
\bfinally\s*\{(テスト内でリソースのクリーンアップに使われているか確認します)。
基本概念
1. テスト構成(test と group)
-
test:テストの基本単位です。test('description', () { // assertions }); -
group:テストを論理的なまとまりに整理するために使います。- グループは入れ子にできます。
- 説明は連結されます(例:「グループの説明 テストの説明」)。
setUpとtearDownの呼び出し範囲を指定できます。- 命名:クラス名に対応するグループには
PascalCaseを使います(例:group('MyClient', ...))。 - 単一のグループを避ける:ファイル内のすべてのテストを囲む
group呼び出しがそれだけの場合は、追加しないでください。- 注:自分が作成していない既存コードをクリーンアップする際、明示的に依頼されない限りグループを削除しないでください。差分が大幅に増え、ほとんどのエンジニアにとって望ましくない結果になる可能性があります。
-
テストの命名
test('ここにテスト名',:- 「test」のような冗長な接頭辞は避けてください。代わりに
groupを使います。 - 期待される動作や結果を説明に含めてください(例:
'throws StateError'、'adds API key to URL')。 - グループ名と連結したときに、自然に読める説明にしてください。
- 「test」のような冗長な接頭辞は避けてください。代わりに
-
名前付きパラメーターの配置:
testとgroupの呼び出しでは、名前付きパラメーター(例:testOn、timeout、skip)を説明文字列の直後、コールバックのクロージャーより前に配置してください。テストロジックを最後に置くことで、可読性が向上します。test('description', testOn: 'vm', () { // assertions });
2. ライフサイクルメソッド(setUp、tearDown)
setUp:現在のgroup(および入れ子のグループ)にある各testの_前_に実行されます。tearDown:現在のgroupにある各testの_後_に実行されます。setUpAll:グループ内のテストが始まる_前_に1回実行されます。tearDownAll:グループ内のすべてのテストが終わった_後_に1回実行されます。
ベストプラクティス:
- テストの分離性を確保するため、状態のリセットには
setUpを使います。 - リセットせずにテスト間で変更可能な状態を共有しないでください。
3. リソースのクリーンアップ
test本体の_内部_で作成したリソースをクリーンアップするには、try-finallyブロックの代わりにaddTearDownの使用を検討してください。
避ける例:
test('can create and delete a file', () {
final file = File('temp.txt');
try {
file.writeAsStringSync('hello');
expect(file.readAsStringSync(), 'hello');
} finally {
if (file.existsSync()) file.deleteSync();
}
});
推奨例:
test('can create and delete a file', () {
final file = File('temp.txt');
// Register teardown immediately after resource creation intent
addTearDown(() {
if (file.existsSync()) file.deleteSync();
});
file.writeAsStringSync('hello');
expect(file.readAsStringSync(), 'hello');
});
4. 設定(dart_test.yaml)
dart_test.yaml ファイルでテストランナーを設定します。一般的な設定には次のものがあります。
プラットフォーム
テストを実行する場所(vm、chrome、node)を指定します。
platforms:
- vm
- chrome
タグ
テストを分類して、特定のサブセットを実行します。
tags:
integration:
timeout: 2x
コードでの使用例:
@Tags(['integration'])
import 'package:test/test.dart';
タグの実行:dart test --tags integration
タイムアウト
テストのデフォルトのタイムアウトを設定します。
timeouts:
2x # Double the default timeout
5. ファイル名
- テストランナーに認識されるには、テストファイル名の末尾を
_test.dartにする必要があります。 - テストは
test/ディレクトリに配置します。
6. テスト設計、シーム、実際のテストダブル
- テストのシームの使い分け(パッケージ統合テストと
lib/src/のユニットテスト):- パッケージレベルおよび統合テスト:
package:<pkg>/<pkg>.dart(公開エントリーポイントのシーム)をインポートし、lib/<pkg>.dartのエクスポートは 公開利用者向けのものに厳密に限定します。 - サブシステムのユニットテスト(
lib/src/):エクスポートされていないパーサー、ステート マシン、データモデル、またはインターフェースがシンプルで内部の動作が豊富なアルゴリズムなど、 内部の深いモジュール(package:<pkg>/src/<subsystem>.dart)をインポートして、 エッジケースを直接ユニットテストします。呼び出し元が1つだけの薄いヘルパーは、それを所有する モジュールのエントリーポイントを通じてテストします。
- パッケージレベルおよび統合テスト:
- 実際の実装とファーストパーティのフェイク(「失敗を検出できるテスト」):実際の依存関係と
ファーストパーティのフェイクを使って、実運用の契約が破られたときにテストが失敗するようにします。
ファイルシステム I/O には
package:test_descriptor(d.sandbox、d.dir、d.file)またはDirectory.systemTemp.createTempSync()(必ずaddTearDown(() => tempDir.deleteSync(recursive: true))に登録)を、サービスには インメモリデータベースまたはループバックのHttpServerインスタンスを、HTTP にはpackage:http/testing.dart(MockClient)を、カスタムインターフェースには手書きのフェイクや スタブを使用し、DOM および JS/Wasm との相互運用には@TestOn('browser')を使用します。 - 決定論的な非同期処理とマッチャーの診断(
fakeAsyncとpackage:checks):ユニットテストでは、実際のawait Future.delayed(...)による待機を 避けてください(テストが不安定になり、テストスイートが遅くなります)。代わりにfakeAsync(package:fake_async)、制御可能なCompleter、または注入したクロック/ランナーの コールバックを使用します。Streamの期待値は、エラーが伝播しないstream.listen(expectAsync1(...))を未待機で使うのではなく、emitsInOrder/expectLaterで検証します。また、expect(x == y, isTrue)や手動のtry { ...; fail('...'); } catch (e) {}ブロックよりも、具体的なマッチャー (throwsA(isA<StateError>().having((e) => e.message, 'message', contains('...'))),completion(...)、unorderedEquals(...))を優先します。 - 動作・構造・関係性のアサーション(変更検出テストは行わない):
- 合成入力でコンシューマーの動作をテストする:定数、設定、モデルを_使用する_コードの観測可能な出力、状態遷移、境界条件を、合成ユニット入力で検証します(例:バリデーターに280文字と281文字を渡す、またはリポジトリ分類器に合成一時ディレクトリを渡す)。定数リテラルをそのまま繰り返すだけの同語反復的な変更検出テスト、静的な設定セット内の項目の所属を固定するテスト(
publishedPackages.contains('foo'))、本番環境の特定のエンティティ名をハードコードするテスト(classifyRepo('specific-repo'))、あるいは単一の静的テンプレート定数内のアクション名、CLI フラグ、固定されたツールのバージョン/SHA を部分文字列で照合するテストは作成しないでください。設定セット、テンプレート定数、データセットの更新によって既存の変更検出アサーションが失敗した場合は、そのリテラルを一致するように更新するのではなく、変更検出アサーションを削除してください。 - 実データとテンプレートの構造・関係性の不変条件を検証する:独立して変化するライブカタログ、JSON データセット、レジストリ、標準テンプレートをテストする場合は、件数(
hasLength(33))、リテラルの設定項目、単一の定数テンプレート内の部分文字列をハードコードするのではなく、構造の妥当性とアーティファクト間の関係性に関する不変条件を検証してください(例:isNotEmpty、必須スキーマキーの存在、mergedCount + openCount == totalCount、またはワークフローテンプレートのジョブ ID とブランチ保護チェック名の一致)。件数や値の完全一致は、決定的なユニット入力に限ってください。
- 合成入力でコンシューマーの動作をテストする:定数、設定、モデルを_使用する_コードの観測可能な出力、状態遷移、境界条件を、合成ユニット入力で検証します(例:バリデーターに280文字と281文字を渡す、またはリポジトリ分類器に合成一時ディレクトリを渡す)。定数リテラルをそのまま繰り返すだけの同語反復的な変更検出テスト、静的な設定セット内の項目の所属を固定するテスト(
- 直接実行・レンダリングによる検証:関数の呼び出し、CLI コマンドの実行、コンポーネントの直接レンダリングによって、実行時の動作、制御フロー、UI/CLI の出力を検証してください。ファイルテキストの直接読み取り(
readAsStringSync())は、静的ドキュメントとのずれ(README.mdの--helpブロック)、ビルド/パッケージメタデータの同期(BUILD、pubspec.yaml)、コードジェネレーターの入力/出力フィクスチャにのみ使用してください。
よく使うコマンド
dart test:すべてのテストを実行します。dart test test/path/to/file_test.dart:特定のファイルのテストを実行します。dart test --name "substring":説明に一致するテストを実行します。
関連スキル
dart-test-fundamentals は、テストの構成と設定に関する基本スキルです。テスト内でアサーションを書く場合は、次を参照してください。
- dart-matcher-best-practices:従来の
package:matcher(expect呼び出し)を使用しているプロジェクトで活用してください。
コメント
Markdown 対応