mcpField
D
dart-matcher-best-practices

dart-matcher-best-practices

github.com/kevmooテスト・品質148266 回インストール2 週間前に更新
リポジトリ

dart-matcher-best-practices について

package:matcher を使った Dart テストの作成と改善を支援するスキルです。コレクション、型、プロパティ、非同期処理、例外のアサーションを扱い、既存のチェックを変更しない条件も示します。

主な機能

  1. 01コレクション用マッチャーの選択
  2. 02型とオブジェクトプロパティの検証
  3. 03非同期結果のアサーション
  4. 04throwsA による例外テスト
  5. 05リファクタリングを避ける条件の判別

活用シーン

  1. 01手動の長さチェックを hasLength に置換
  2. 02Map 検索アサーションの改善
  3. 03Future の結果と例外のテスト

インストール

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

$npx skills add https://github.com/kevmoo/dash_skills --skill dart-matcher-best-practices

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

同梱ファイルevals/evals.json

詳細

Dart Matcher のベストプラクティス

このスキルを使用する場面

次の場合にこのスキルを使用してください。

  • expect と package:matcher を使ってアサーションを記述する場合。
  • レガシーな手動チェックを、よりすっきりしたマッチャーに移行する場合。
  • わかりにくいテスト失敗をデバッグする場合。
使用しない場面(適用を控えるためのガードレール)

次の場合は、このスキルを適用したりアサーションをリファクタリングしたりしないでください。

  • コレクション以外のプロパティ:プロパティ名が .length でも、コレクション以外のクラスに属している場合(例:Vector の幾何学的なスカラーの大きさ、バッファーの長さ、線分の距離)。hasLength(n) が想定するのは、Iterable、Map、または String の int です。
  • 独自ドメインの真偽値:任意のドメインプロパティを検査する真偽値チェックの場合(例:expect(switch.isEnabled, isTrue))。存在しないマッチャーを作らないでください。
  • 別のアサーションフレームワーク:パッケージで package:checks(check(x)...)を採用している場合。checks を package:matcher に戻さないでください。
  • すでに慣用的なアサーション:テストですでに宣言的なマッチャー(hasLength、isEmpty、isNotEmpty、contains、containsPair、isA、throwsA)を使用している場合。適用を控え、変更を一切加えないでください。

検出

マッチャーの使い方を改善できる箇所を見つけるには、最適でないパターンを検索します。

最適でない長さチェック

hasLength を使用すべき長さチェックを検索します。

  • 正規表現:expect\([^,]+.length,\s*
最適でない真偽値チェック

専用のマッチャーがある真偽値プロパティのチェックを検索します。

  • 正規表現:expect\([^,]+.isEmpty,\s*(true|equals\(true\))
  • 正規表現:expect\([^,]+.isNotEmpty,\s*(true|equals\(true\))
  • 正規表現:expect\([^,]+.contains\(.*\),\s*(true|equals\(true\))
最適でない Map の検索

containsPair を使わずに手動で Map を検索している箇所を検索します。

  • 正規表現:expect\([^,]+\[.*\],\s*

基本的なマッチャー

1. コレクション(hasLength、contains、isEmpty、unorderedEquals、containsPair)
  • hasLength(n):

    • expect(list.length, n) よりも expect(list, hasLength(n)) を優先します。
    • 失敗時のエラーメッセージが改善されます(実際のリストの内容が表示されます)。
  • isEmpty / isNotEmpty:

    • expect(list.isEmpty, true) よりも expect(list, isEmpty) を優先します。
    • expect(list.isNotEmpty, true) よりも expect(list, isNotEmpty) を優先します。
  • contains(item):

    • 手動で反復処理せずに、存在を確認できます。
    • expect(list.contains(item), true) より優先します。
  • unorderedEquals(items):

    • 順序に関係なく内容を確認できます。
    • expect(list, containsAll(items)) より優先します。
  • containsPair(key, value):

    • Map に特定のキーと値のペアが含まれていることを確認します。
    • expect(map[key], value) や expect(map.containsKey(key), true) による確認より優先します。
2. 型チェック(isA<T> と TypeMatcher<T>)
  • isA<T>():

    • インラインアサーションに適しています:expect(obj, isA<Type>())。
    • TypeMatcher<Type>() より簡潔で読みやすくなります。
    • .having() を使って制約を連結できます。
  • TypeMatcher<T>:

    • トップレベルで再利用可能なマッチャーを定義する場合に適しています。
    • const を使用してください:const isMyType = TypeMatcher<MyType>();
    • ここでも .having() を連結できますが、生成されるマッチャーは const ではありません。
3. オブジェクトのプロパティ(having)

プロパティを確認するには、isA<T>() またはほかの TypeMatcher で .having() を使用します。

  • 説明的な名前:読みやすさを高めるため、クロージャー内では p0 のような汎用的な名前ではなく、(e) => e.message のような意味のあるパラメーター名を使用します。
expect(person, isA<Person>()
    .having((p) => p.name, 'name', 'Alice')
    .having((p) => p.age, 'age', greaterThan(18)));

これにより、どのプロパティが失敗したかを正確に示す、詳細な失敗メッセージが表示されます。

4. 非同期アサーション
  • completion(matcher):

    • Future の完了を待ち、その値を確認します。
    • テストを続行する前に Future が完了するよう、await expectLater(...) を優先してください。
    • await expectLater(future, completion(equals(42)))。
  • throwsA(matcher):

    • Future または関数が例外をスローすることを確認します。
    • await expectLater(future, throwsA(isA<StateError>()))。
    • expect(() => function(), throwsA(isA<ArgumentError>()))(同期関数が例外をスローする場合は expect で問題ありません)。
5. 例外のテスト(try/catch と fail() の併用を避ける)
  • 命令的な try/catch より throwsA を優先してください:
    • 古いテストでは、次のように書かれていることがよくあります。 try { fn(); fail('should throw'); } catch (e) { expect(e, isA<T>()); }。
    • 宣言的なマッチャーに置き換えてください。
      // 同期処理:
      expect(() => parseData('invalid'), throwsA(isA<FormatException>()));
      
      // 非同期処理:
      await expectLater(client.fetch('bad-url'), throwsA(isA<HttpException>()));
      
    • よく使用される組み込みの例外マッチャーには、throwsArgumentError、 throwsStateError、throwsRangeError、throwsFormatException、 throwsNoSuchMethodError、throwsUnsupportedError があります。
6. expectLater の使用

非同期の動作をテストする際は、処理が適切な順序で実行されるよう、await expectLater(...) を使用してください。

// GOOD: Waits for future to complete before checking side effects
await expectLater(future, completion(equals(42)));
expect(sideEffectState, equals('done'));

// BAD: Side effect check might run before future completes
expect(future, completion(equals(42)));
expect(sideEffectState, equals('done')); // Race condition!

原則

  1. 失敗時のわかりやすさ:明確なエラーメッセージを生成するマッチャーを選んでください。
  2. 手動ロジックを避ける:アサーションに if 文や for ループを使用せず、マッチャーに処理させてください。
  3. 具体的なマッチャー:利用可能な中で最も具体的なマッチャーを使用してください(例:マップのキーを手動で確認する代わりに containsPair を使用します)。

関連スキル

  • dart-test-fundamentals:テストの構成、ライフサイクル、設定に関する基本概念です。

コメント