JUSTWIT
000
← ブログ一覧に戻る
技術解説

生成AIでFlutterのWidgetテストを量産する — 観点設計とモック境界のノウハウ

#Flutter#テスト#生成AI#Widgetテスト#品質保証

はじめに

生成AIにコードを書かせる中で、もっとも費用対効果が高いと感じたのがテストコードの量産です。実装コードと違ってテストは構造が定型的で、観点さえ正しければ量が品質に直結します。

タイマーアプリの開発では、Widgetテスト・ユニットテスト・ゴールデンテストを生成AI主体で整備し、カバレッジ 80% 到達までの工数を体感で半分以下にできました。本記事はその過程で固まったノウハウ集です。


原則: 「テストを書いて」ではなく「観点を検証して」

生成AIに「このファイルのテストを書いて」と頼むと、getter の値を確認するような通ることが自明なテストが量産されます。カバレッジは上がりますが、バグは捕まえられません。

効くのは、人間が観点を列挙して検証だけを任せる形です。

対象: SessionHistoryRepository
観点(それぞれ独立したテストにする):
1. 同一日の複数セッションが日付キーで集約される
2. 日付跨ぎ(23:50開始→00:10終了)のセッションは開始日に帰属する
3. 履歴が空のとき週間サマリーはゼロ埋めで7要素返す
4. 不正な保存データ(壊れたJSON)は握りつぶさず SessionDataException
構成: Arrange / Act / Assert の3節コメント付き

観点の列挙は 5〜10 分の作業ですが、これを渡すかどうかで生成されるテストの価値が桁で変わります。境界値(日付跨ぎ・空・不正データ)は人間が指定しないとまず出てきません。

モック境界を先に切る

Flutterのテストで生成AIがつまずくのは、ほぼプラットフォーム依存の扱いです。時計・通知・振動・ライフサイクルをそのまま触るコードはテスト不能なので、先に境界を切っておきます。

/// 時計は必ず注入する。DateTime.now() の直接呼び出しを禁止すると
/// fake_async でのテストが全テストで可能になる
abstract class Clock {
  DateTime now();
}

/// 通知・振動はインターフェース越しに。テストでは呼び出し記録だけ検証する
abstract class AlertService {
  Future<void> notify(SessionPhase phase);
  Future<void> vibrate();
}

この2つの境界があるだけで、「25分経過で通知が1回だけ発火する」のような時間系のテストを生成AIが正確に書けるようになります。逆に境界がないと、Future.delayed で実時間を待つ壊れやすいテストを平気で出してくるので注意が必要です。

Widgetテストで固定しているレビュー観点

生成されたテストをレビューする際、毎回チェックしている項目です。

  1. pumpAndSettle() の乱用: 無限アニメーション(進行中のタイマーリングなど)があると永久に settle せずタイムアウトします。pump(duration) の明示指定に直させる
  2. 実時間待ち: await Future.delayed が入っていたら書き直し。fake_asynctester.pump で時間を進める
  3. 文言でのWidget検索: find.text('開始') は i18n 変更で壊れます。Key ベースの検索に統一
  4. アサーションの弱さ: expect(find.byType(X), findsOneWidget) だけで終わっているテストは、状態の中身(表示時間・カウント値)まで検証させる

このリスト自体をプロジェクトのルールファイルに書いておくと、2回目以降は最初から準拠したテストが出てきます。

ゴールデンテストは「差分の説明」までさせる

UIの回帰検知にはゴールデンテストを使っています。生成AI活用で便利なのは、ゴールデン更新時に差分の言語化をさせることです。

このゴールデン差分画像を見て、変更点を列挙してください。
意図した変更(ボタン角丸の変更)以外の差分があれば指摘してください。

意図しないフォントフォールバックの変化や 1px のレイアウトずれを言葉で拾ってくれるため、「差分があるけど多分大丈夫」で更新してしまう事故が減りました。

テストが落ちたときの運用

生成AIにテスト失敗を直させるときの鉄則は、「テストを直すのか実装を直すのか」を人間が先に宣言することです。宣言せずに投げると、実装のバグをテスト側の期待値変更で「解決」されることがあります。

このテスト失敗は実装側のバグです。テストの期待値は変更禁止。
実装を修正してください。修正方針を先に1行で述べること。

効果の実感値

項目Before(手書き)After(観点+生成)
ユニットテスト 1観点あたり10〜20分2〜3分(レビュー込み)
カバレッジ80%到達2〜3日1日弱
境界値テストの網羅感書き手の経験に依存観点リストの品質に依存

「観点リストの品質に依存」という点は重要で、テスト設計力はむしろ以前より問われるようになりました。手を動かす時間が減った分、観点出しに時間を使えるのが本質的な改善です。

まとめ

  • テスト生成は「観点の検証」として依頼する。観点列挙は人間の仕事
  • 時計・通知などのモック境界を先に切ると生成品質が安定する
  • pumpAndSettle・実時間待ち・文言検索は生成テストの三大アンチパターン
  • テスト失敗時は「どちらを直すか」を宣言してから修正させる