Flutter、AI、テスト:より速く、より安全にコーディングする新手法

Sommaire
- 1. なぜ私が Flutter でのコーディング方法を見直したか
- 2. コード以前:意図を定義する(spec-driven development)
- Objectif
- Flux principal
- Règles métier
- 3. AI は「超高速なジュニア開発者」…しかし枠組みが必要
- 4. このワークフローにおけるテストの中心的役割
- 4.3. 完全なワークフローの図
- 4.4. テストプロセスでスクリーンショットをより活用する方法
- 5. Flutter ベースでの具体例
- 6. なぜこの方法がすべてを変えるのか
- 7. ドキュメントの更新:ループを閉じる
- 8. 限界(正直に言うと)
- 9. 更に進めるために:Spec Kit と AI による仕様駆動
- 結論
- 参考リンク
2025年、IDE に大量の AI エージェントが導入されたことで私たちのコーディングのやり方は変わりました。しかし、しっかりした枠組みがなければ、そのスピードはリスクになり得ます。この記事では、私が Flutter プロジェクトを次の三本柱で構成した方法を紹介します:明確な仕様, 自動テスト, AI 支援。結果として、より信頼性が高く、より速く、そしてずっと快適なワークフローが実現しました。
1. なぜ私が Flutter でのコーディング方法を見直したか
Flutter を始めた頃、私は主に UX/UI、アニメーション、パフォーマンスに注力していました。しかし AI の到来で新たな課題が現れました:
AI と協働してアプリの安定性を損なわないにはどうすればいいか?
より速くコーディングするのは簡単です。だが、"自信を持って速く" コーディングするのは全く別物です。
そこで私は古い考えを思い出しました:明確な意図なしにはコードを書かない。
2. コード以前:意図を定義する(spec-driven development)
2.1. なぜまず仕様が必要なのか?
多くの AI プロンプトは文脈が不足して失敗します。AI は私たちの望みを推測しなければならないからです。
仕様ファイルに意図を明記すると、ワークスペースが明確で管理された環境に変わります。
仕様には次のようなものが含まれます:
- 画面の説明とその役割
- ユーザーフロー
- ビジネスルール
- エッジケース
- 処理すべきエラー
- 技術的制約
最小限の仕様の例 :
# Story: Scan d'une carte Pokémon (PSA)
## Objectif
L'utilisateur scanne une carte Pokémon gradée PSA (via le QR code ou le code de certification) pour l'ajouter à sa collection numérique.
## Flux principal
1. L'utilisateur ouvre l'écran de scan.
2. La caméra s'active automatiquement.
3. Le QR code / code PSA est détecté.
4. L'app récupère les données de la carte via l'API PSA (nom du Pokémon, set, numéro, grade, numéro de certification, éventuellement visuel).
5. L'app affiche une fiche détaillée avec les infos PSA + les infos propres à l'app (collection, tags, notes…).
## Règles métier
- Si la carte est déjà dans la collection → afficher un dialogue « Cette carte PSA est déjà dans ta collection ».
- Si l'API PSA ne répond pas → afficher une erreur explicite et proposer de réessayer.
- Si le QR/code est illisible → afficher un message d'erreur et permettre de rescanner.
- Le scan + appel API PSA doivent s'effectuer en moins de 500 ms dans des conditions normales.
このファイルはあなたと AI エージェントの両方にとっての 真実のソース となります。
3. AI は「超高速なジュニア開発者」…しかし枠組みが必要
3.1. AI が非常に得意なこと
- クラスやサービスの生成
- 再利用可能なウィジェットの作成
- 面倒なボイラープレートの作成
- コードのリファクタリング
- よりクリーンなパターンの提案
3.2. 文脈がないと苦手なこと
- ビジネスルールの取り扱い
- 全体的な一貫性の維持
- エッジケースの想定
- 仕様なしで複雑なフローを理解すること
だからこそ良い AGENTS.md ファイルやアクセス可能な明確な仕様が重要です。あなたがフレームを作り、AI がコードを生成します。
4. このワークフローにおけるテストの中心的役割
AI エージェントを信頼できる開発者に変えるために、テストはあなたの セーフティネット になります。
4.1. Flutter テストの三層
🟦 ユニットテスト
ビジネスロジックをテストするために:
import 'package:flutter_test/flutter_test.dart';
import 'package:myapp/domain/pokemon_service.dart';
void main() {
group('PokemonService', () {
test("ajout d'une carte PSA déjà présente", () {
final service = PokemonService();
service.addFromPsaCert('PSA-123');
final result = service.addFromPsaCert('PSA-123');
expect(result.isSuccess, false);
expect(result.errorCode, 'already_exists');
});
});
}
🟨 ウィジェットテスト
アプリ全体を起動せずに UI とインタラクションを検証するため。例:スキャン画面が API から返された PSA カードのプレビューを正しく表示することを確認。
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:myapp/ui/scan_screen.dart';
import 'package:myapp/domain/pokemon_service.dart';
class FakePokemonService extends PokemonService {
@override
Future<PokemonCard> fetchFromPsa(String certId) async {
return PokemonCard(
id: 'PSA-123',
name: 'Pikachu',
setName: 'Base Set',
grade: '10',
);
}
}
void main() {
testWidgets('affiche la fiche PSA après scan réussi', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: ScanScreen(
pokemonService: FakePokemonService(),
),
),
);
// On simule ici le résultat d'un scan réussi (par ex. via un callback).
final state = tester.state<ScanScreenState>(find.byType(ScanScreen));
await state.onScanResult('PSA-123');
await tester.pumpAndSettle();
expect(find.text('Pikachu'), findsOneWidget);
expect(find.text('Base Set'), findsOneWidget);
expect(find.text('PSA 10'), findsOneWidget);
});
}
🟩 統合テスト
実際のユーザーフローをシミュレートするため:アプリを開き、スキャン画面へ移動し、PSA カードをスキャンし、詳細を確認し、必要ならイシューで利用できる スクリーンショット を取得する。
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:myapp/main.dart' as app;
import 'dart:io';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('parcours complet de scan PSA', (WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
// Aller sur l'écran de scan
await tester.tap(find.byKey(const Key('go-to-scan-button')));
await tester.pumpAndSettle();
// Ici tu peux mocker la couche caméra / scan pour renvoyer un certId PSA.
// Par exemple via un FakeScanner ou un flag de debug.
// Une fois le scan simulé, on attend le chargement de la fiche.
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.text('Pikachu'), findsOneWidget);
// Prendre un screenshot et le stocker dans un dossier lié à une issue
final binding = IntegrationTestWidgetsFlutterBinding.instance;
final bytes = await binding.takeScreenshot('scan_psa_flow');
final dir = Directory('test_screenshots/issue-123');
if (!dir.existsSync()) {
dir.createSync(recursive: true);
}
final file = File('test_screenshots/issue-123/scan_psa_flow.png');
await file.writeAsBytes(bytes);
});
}
このパターンは非常に強力です:特定のバグごとに 統合シナリオ+スクリーンショット を専用フォルダ(例: test_screenshots/issue-123/)に保存できます。これはリグレッションを文書化し、チーム内(または将来の自分)とのコミュニケーションを容易にします。
4.2. AI とテストの完全なサイクル
- 仕様を書く/補完する。
- 仕様を AI エージェントに処理させる。
- AI がコードを提案する。
- テストを実行する(ユニット、ウィジェット、統合)。
- すべてが緑 → マージ。
- テストが失敗 → コードまたは仕様の調整。
4.3. 完全なワークフローの図
このワークフローは単純なループで要約できます:
重要な考え方:ドキュメントと仕様は一度書いて終わりにするものではありません。コードとテストと同じペースで進化させるべきです。
4.4. テストプロセスでスクリーンショットをより活用する方法
統合テストの最後にスクリーンショットを撮るのは単なる「見た目のため」だけではありません。本当に役立つ作業ツールにできます:
- 📌 ドキュメント化されたバグ:イシュー(GitHub、Jira…)を作成するとき、テストで生成したスクリーンショット(
test_screenshots/issue-123/scan_psa_flow.png)を添付できます。問題が発生した時点の画面が正確にわかります。 - 🧪 「シンプルだが効果的な」ビジュアルリグレッション:本格的なビジュアルテスト環境を整えなくても、ブランチやバージョン間でスクリーンショットを比較するだけで目立つ欠落(ボタンが消えた、テキストがはみ出している等)を検出できます。
- 🧭 オンボーディングとストーリーテリング:整頓された
test_screenshotsフォルダは、アプリを起動せずにフローの「見た目」を素早く示せる手段です。新しい開発者や QA を迎え入れる時、自分用のドキュメント作成時に便利です。 - 🔁 トレーサビリティ:パスに含まれるイシュー ID(
issue-123)は spec → test → キャプチャ → チケット の自然なリンクを作ります。チケットをクローズするときに、議論の中で「前後の写真」としてキャプチャを残せます。
さらに進めるなら、これらのスクリーンショットを CI のアーティファクトとして自動アップロードしたり、重要なフローについて PR コメントに自動投稿することもできます。
5. Flutter ベースでの具体例
テストと生成コードの構成例(実際の Flutter プロジェクト):
lib/
domain/
pokemon_service.dart
ui/
scan_screen.dart
pokemon_card.dart
data/
pokemon_repository.dart
test/
unit/
pokemon_service_test.dart
widget/
scan_screen_test.dart
integration/
scan_flow_test.dart
specs/
scan_pokemon.md
add_to_collection.md
error_cases.md
CI 側では:
flutter test
flutter test integration_test
このような構成にすると AI と開発者の共同作業が非常にスムーズになります。
6. なぜこの方法がすべてを変えるのか
より速く
AI が面倒な作業を処理します。あなたはロジック、ビジネス、UX に集中できます。
より信頼できる
仕様とテストが AI を安全なツールに変えます。
より保守しやすく
すべてがテストされていれば、リファクタリングは本当に楽しくなります。
ビジネスにより合致する
思考は実装からではなく仕様から始まります。
7. ドキュメントの更新:ループを閉じる
ワークフローでよく抜け落ちるステップがあります:作業が完了したらドキュメントに戻ること。
次のような変更を行ったら必ず:
- 新しいビジネスルールを追加したとき、
- バグを修正したとき、
- フローを変更したとき、
...次を 更新 するべきです:
- 該当する仕様(spec)、
- 必要に応じてコンテキストファイル(AGENTS.md、アーキテクチャ文書)、
- 関連するテストとスクリーンショットへの参照、
これがドキュメントをコードと整合した 生きたドキュメント に変えます。AI も恩恵を受けます(より良い文脈)、そして 6 か月後にプロジェクトに戻ってきたときにあなた自身も助かります。
8. 限界(正直に言うと)
- 小さな機能には過剰な場合がある。
- 仕様が悪いと、AI が良くても結果は悪い。
- 複雑な UI はまだ手作業の繊細さが必要。
- 少しの規律が求められる(しかし保守性の利得は大きい)。
9. 更に進めるために:Spec Kit と AI による仕様駆動
このアプローチをさらに押し進めたいなら、GitHub のオープンソースツール Spec Kit をチェックしてみてください。これは spec-driven development を念頭に置いたツールです:
- まず詳細な仕様(「何を」「なぜ」)を書きます。
- ツールが技術的プランとタスクリストを派生するのを助けます。
- その後、AI エージェントにこれらのアーティファクトからコードを生成させます。
- 生成されたものを既存のテストと連携させます。
考え方は先ほどのものと同じですが、複数の AI アシスタントと連携することを想定した汎用ツールとして形式化されています。現時点では Spec Kit は主にウェブ向けをターゲットにしていますが、その哲学は他のコンテキストでも100% 再利用可能です:
- 強固な 仕様 から始める、
- 意図(ドキュメント)と実装(コード)を 明確に分離 する、
- AI は 高速な実行者 として使い、真実のソースにしない、
- すべてを 自動テスト でロックする。
将来的には次のような流れが想像できます:
- Spec Kit(または同等品)が上位レベルで機能を説明する、
- AI エージェントが Flutter 層(UI + ロジック)を生成する、
- Flutter テストが実装が仕様に合っているかを検証する。
要するに、もしあなたがリード開発者で AI を使ったワークフローの工業化を考えているなら、この種のツールを試す価値は十分にあります。少なくとも仕様の考え方がどう進化するかを見るために。
結論
Flutter 開発の未来は単に「AI でより速くコーディングする」だけではありません。こうなります:
意図を持ってコーディングし、支援を受け、確実にコーディングする。
ワークフローでは:
- 仕様が「何を望むか」を示し、
- AI が「どう実装するか」を提案し、
- テストが「正しくできているか」を保証する。
あなたはすでにこのタイプのワークフローを Flutter で試しましたか?ぜひあなたのフィードバックを読みたいです。
参考リンク
- Flutter のテストドキュメント : Flutter のテストドキュメント
- Flutter テストに関する完全な記事 : Flutter テストに関する完全な記事
- ベストプラクティス : 包括的な Flutter アプリテストのガイド(ベストプラクティスと実証済み戦略)
- Spec Kit(spec-driven development + IA) : Spec Kit GitHub リポジトリ
- GitHub Spec Kit チュートリアル — ひどい AI コードはもう終わり!
コメント
読み込み中...