簡単な自己紹介
こんにちは!
私は開発経験1年程度のエンジニアです。
日々の開発経験を通じて、私が経験したことや、学んだ技術情報を展開していくために
今回から記事を書いていこうと思います!
(Qiitaの書き方も上手くなっていければいいな)
今回の記事
アプリ制作をしていて、初めてintegration_testを使用して
テストコードを書くことがあったので、その時に困ったことを備忘録として
残していこうと思います!
前知識:pumpとpumpAndSettleの違い
・pump()
UIを1回だけ更新する
await tester.pump(); // 1回だけフレーム更新
await tester.pump(const Duration(milliseconds: 100)); // 100ms進めて1回更新
・pumpAndSettle()
全てのアニメーション・タイマーが停止するまで待機する
await tester.pumpAndSettle(); // 全部止まるまで永遠に待つ
await tester.pumpAndSettle(const Duration(seconds: 5)); // 最大5秒まで待つ
動作:
- pump()を実行してUIを更新
- 描画フレームが存在しているかチェック
- 存在していたら再度pump()
- 全部止まるまで2-3を永遠に繰り返し
やっていたこと
バックエンドからのデータ取得があるウィジェットに対して、
ウィジェットの生成を待ってから後続のテストを流そうとしていた。
最初:引数なしでpumpAndSettleを使っていた
await tester.pumpAndSettle(); //これで行けるはず
あれ、ウィジェットの生成はされているはずなのにテストに失敗した、、
もしかして、データがまだ表示されていないから?
⭐️今回の失敗の原因
1. Widget の描画完了 ≠ データの取得完了 だった
2. Widget自体はすぐ描画完了
3. pumpAndSettle()は「描画終わったね!」で終了
4. でも裏ではDBからデータ取得中...
なるほど!
取得したいデータがとれていない状態でそのデータを扱おうとしていた訳だ。
あれ、では待機時間を設定して待ってあげれば成功するのでは?
やってみよう
await tester.pumpAndSettle(const Duration(seconds: 5)); //これでどうだ
→成功!
これでCI/CD環境でテストが通せる、はず。
→失敗!
どーなってるんやこれ、、
状況整理
・ローカル環境 : DB処理5秒 → 成功
・CI/CD環境 : DB処理5秒 → タイムアウト
あ、環境差のことを考えていなかった。
解決策
Before(失敗パターン)
await tester.tap(find.text('データ取得'));
await tester.pumpAndSettle(const Duration(seconds: 5)); // 祈りの5秒
After(成功パターン)
await tester.tap(find.text('データ取得'));
await tester.pump(); // とりあえずローディング開始
// ちゃんとデータが表示されるまで待つ
for (int i = 0; i < 20; i++) {
await tester.pump(const Duration(milliseconds: 100));
if (find.bykey(ListTile).evaluate().isNotEmpty) break; // データ来た!
await Future.delayed(const Duration(milliseconds: 500));
}
データ取得を待てばよかったんだね
今回の学び
・時間で推測すると、環境差で失敗することがある
・実際に欲しいものが表示されるまで待つのが正解
・Widget描画とデータ取得は別次元の話
感想
テストコード作成、なかなかうまく行かないことが多いなぁ。。
これからも、誰かの役に立てるような記事を投稿していければいいなと
思いながら定期的に更新していこうと思います!