dart-migrate-to-checks-package について
dart-migrate-to-checks-packageは、Dartのテストをpackage:matcherからpackage:checksへ移行する開発者向けのスキルです。アサーション構文の対応を示し、FutureとStreamの非同期検証も扱います。
主な機能
- 01一般的なアサーション構文の対応
- 02依存関係とインポート変更の案内
- 03カスケードによるプロパティ検査
- 04Futureの非同期アサーション解説
- 05Streamの順次アサーション解説
- 06移行検証チェックリスト
活用シーン
- 01expectアサーションをcheckへ変換
- 02FutureとStreamのテストを書き換え
- 03移行後の解析とテストを確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dhruvanbhalara/ skills --skill dart-migrate-to-checks-packageインストール後、Claude Code で/dart-migrate-to-checks-packageを入力して読み込まれたか確認します。
詳細
目次
package:checks に移行する理由
package:checks は、Dart チームがメンテナンスしている最新のアサーションライブラリで、package:matcher に代わるものです。従来のマッチャーに比べて、次のようなメリットがあります。
- 流暢な API:ネストしたヘルパー関数の代わりに Dart のメソッドを使い、自然にアサーションを連鎖できます。
- 型安全性:期待されるプロパティに対するコンパイラのチェックを活用でき、テストでの型の不一致を防げます。
- わかりやすい診断情報:何が、なぜ失敗したのかを正確に示す、非常に明確な失敗メッセージを確認できます(例:コレクションの差異を表示します)。
依存関係の管理
パッケージに checks ライブラリを追加するには、次の手順に従います。
- ターミナルを使って、
checksをdev_dependencyとして追加します。dart pub add dev:checks - 移行対象のすべてのテストファイルで、メインパッケージのエントリーポイントをインポートします。
import 'package:checks/checks.dart'; pubspec.yamlからpackage:matcherの明示的な依存関係を削除します(package:testは推移的にマッチャーをインポートしますが、これは問題ありません)。
アサーションの対応関係と構文のガイドライン
次の対応関係に従って、従来のアサーションを移行します。
| シナリオ | 従来のマッチャー(package:matcher) | 最新の checks(package:checks) |
|---|---|---|
| 等価性 | expect(value, equals(expected)) | check(value).equals(expected) |
| 同一性 | expect(value, same(expected)) | check(value).identicalTo(expected) |
| 型チェック | expect(value, isA<Type>()) | check(value).isA<Type>() |
| null 判定 | expect(value, isNull) | check(value).isNull() |
| 真偽値 | expect(value, isTrue) | check(value).isTrue() |
| 包含 | expect(list, contains(item)) | check(list).contains(item) |
| 文字列の接頭辞 | expect(str, startsWith(prefix)) | check(str).startsWith(prefix) |
| プロパティのチェック | expect(user.name, equals('Alice')) | check(user).has((u) => u.name, 'name').equals('Alice') |
複数のチェックの連鎖
同じ対象に対して個別の期待値ステートメントを書かずに複数のプロパティを検証するには、Dart のカスケード演算子(..)を使います。
check(user)
..has((u) => u.name, 'name').equals('Alice')
..has((u) => u.age, 'age').isGreaterThan(20);
高度な非同期アサーション
1. Future
Future の解決を検証するには、基本となる check ステートメントを必ず await します。
// Verify completion value:
await check(fetchScore()).completes((score) => score.equals(100));
// Verify throws error:
await check(failingTask()).throws<HttpException>().has((e) => e.statusCode, 'statusCode').equals(500);
2. Stream
Stream が出力する複数の項目を順番に検証するには、対象のストリームを package:async の StreamQueue でラップします。
import 'package:async/async.dart';
final queue = StreamQueue(eventStream);
await check(queue).emits((event) => event.equals('first_event'));
await check(queue).emits((event) => event.equals('second_event'));
ワークフロー:構文移行の実行
テストスイートを移行する際は、次のチェックリストに従います。
- 依存関係の設定:
dev:checksをインストールし、dart pub getを実行します。 - 対象ファイルの検索:従来の
expect()呼び出しを含むテストファイルを見つけます。 - インポートの更新:matcher のインポートを置き換えるか、
import 'package:checks/checks.dart'を追加します。 - 単純なケースの変換:基本的な等価性と型の一致を
expectからcheckに変換します。 - カスケードによる最適化:同じオブジェクトを対象とする複数のアサーションをカスケードにまとめます。
- 非同期呼び出しの更新:Future/Stream の非同期チェックを書き換え、
awaitとcompletes()クロージャーを使います。 - コンパイラチェックの検証:
dart analyzeを実行し、型の不一致に関する警告を解消します。 - テストの実行:
dart testを実行し、移行したすべてのアサーションが正常に実行されることを確認します。
例
移行の比較
この例では、従来の matcher 構文から最新の checks 構文へ、完全で忠実度の高いユーザープロフィールのテストケースを移行する方法を示します。
// --- Legacy Matcher Assertion Style ---
import 'package:test/test.dart';
class Account {
final String email;
final List<String> tags;
Account(this.email, this.tags);
}
void badTest() {
final account = Account('[email protected]', ['admin', 'tier1']);
expect(account.email, equals('[email protected]'));
expect(account.tags, contains('admin'));
expect(account.tags.length, equals(2));
}
// --- Modern Checks Assertion Style ---
import 'package:checks/checks.dart';
void goodTest() {
final account = Account('[email protected]', ['admin', 'tier1']);
// Chain validations cleanly using cascades and property extractors
check(account)
..has((a) => a.email, 'email').equals('[email protected]')
..has((a) => a.tags, 'tags').contains('admin')
..has((a) => a.tags.length, 'tags length').equals(2);
}
例外と Future の完了のテスト
import 'package:checks/checks.dart';
import 'package:test/test.dart';
Future<String> fetchRole(bool fail) async {
await Future.delayed(const Duration(milliseconds: 10));
if (fail) throw StateError('Fetch failed');
return 'Manager';
}
void main() {
group('Asynchronous Tests', () {
test('verifies successful completion', () async {
await check(fetchRole(false)).completes(
(role) => role.equals('Manager'),
);
});
test('verifies throwing specific state errors', () async {
await check(fetchRole(true)).throws<StateError>().has(
(e) => e.message, 'message',
).equals('Fetch failed');
});
});
}
コメント
Markdown 対応