【Flutter】pumpWidgetについてのまとめ

Flutter基礎

今回は、WidgetTestで出てくるpumpWidgetについてまとめてみました。
よかったらご覧ください。

PumpWidget

公式

pumpWidget method - WidgetTester class - flutter_test library - Dart API
API docs for the pumpWidget method from the WidgetTester class, for the Dart programming language.

概要

  • 役割
    • 与えらたWidgetを一回レンダリングする
      • それによっての反応をテストする
  • 内容
    • runAppを呼び出しテスト環境に該当のWidgetをセット
    • 内部的に pumpメソッドを呼び出し1フレーム分の更新処理を実行
    • マイクロタスクも全て実行しUI処理を完了させる

処理の流れ

  1. ウィジェットをrunAppで差し替え
  2. フレームを更新(build/layout/paint)
  3. マイクロタスクキューを全部実行
  4. UIが確定してからexpectで検証可能に

マイクロタスクとは?

  • Dartの非同期処理には 2つのキュー があります
    1. マクロタスク(event queue)
      • タイマー(Timer)、I/O(ファイル読み込み、HTTP通信)など
      • 実行は「次のイベントループのターン」まで待たされる
    2. マイクロタスク(microtask queue)
      • Future.microtask()Future(...).then(...) など
      • 今のイベントループ内で即座に処理される(次のフレームを待たない)

イメージするとこんな順番で動きます👇

[現在の処理] → [マイクロタスク全部] → [次のマクロタスク] → [またマイクロタスク] …

1. イベントループの基本

Dart(とJavaScript)はシングルスレッドで動くことが多く、「イベントループ」という仕組みで処理の順番を管理します。

大まかにいうと、1回のループはこんな順番です:

1. 現在の処理を終える
2. マイクロタスクキューを全部処理する
3. マクロタスク(イベントキュー)の先頭を1つ取り出して実行
4. またマイクロタスクキューを全部処理
5. 繰り返し...

2. マイクロタスクとマクロタスクの違い

種類実行タイミング
マイクロタスクFuture.microtask(), Future(...).then(...)今のイベントループ内で、最優先で実行
マクロタスクTimer(Duration.zero, ...), I/O, HTTPリクエスト完了次のイベントループのタイミングで実行

ポイントは、

マイクロタスクは「すぐやる」、マクロタスクは「次のターンまで待つ」 ということ。


3. コードで比較

import 'dart:async';

void main() {
  print('A');

  Future(() => print('B (macro task)'));
  Future.microtask(() => print('C (micro task)'));

  print('D');
}

出力順:

A
D
C (micro task)
B (macro task)

なぜこうなるか?

  1. AD は同期処理
  2. 同期処理が終わると、マイクロタスクを全部処理 → C
  3. その後、マクロタスクを1つ処理 → B

4. Flutterとマイクロタスク

FlutterではUIの変更がよくマイクロタスク経由で行われます。

例えば:

@override
void initState() {
  super.initState();
  Future(() {   // マクロタスク
    setState(() { message = 'loaded'; });
  }).then((_) { // マイクロタスク
    print('state updated');
  });
}
  • Future(...).then(...).then 部分はマイクロタスクで実行
  • この更新処理が実行される前にUIを確認すると古い状態が見えてしまう
  • そこで pumpWidgetフレーム更新後にマイクロタスクキューを空にして 「UIが確定した状態」にしてからテストを進める

5. なぜ重要か

  • UIの反映順序を理解できる(特に非同期処理が多いWidget)
  • テストが不安定になる原因を突き止めやすい
  • pumppumpAndSettle の違いも納得できる (pumpAndSettle はマクロタスクとマイクロタスクが全部終わるまで回す)

PumpとPumpWidgetの違い

  • pumpWidget は、たとえ渡すWidgetが前回と同じでも、 「ツリーを丸ごと作り直す(full rebuild)」 を必ず行う
  • pump は、Flutterの通常描画と同じく、変わった部分だけ差分更新 を行う (同じWidget構造ならビルドされない)

結論

  • pumpWidgetはpumpを呼び出しUIを描画させる
    • その後のexpectメソッドなどで描画されているかをテストする

コメント

タイトルとURLをコピーしました