okaryo.log

FlutterWidgetTestにおいてIndexedStackのような画面に描画されていないがOffstageには存在する要素の確認をする | okaryo.log

FlutterWidgetTestにおいてIndexedStackのような画面に描画されていないがOffstageには存在する要素の確認をする

    #Flutter

はじめに

個人開発しているLazyLoadIndexedStackというFlutterパッケージに新機能を追加して新バージョンをリリースした。

その際にテストを修正する過程で、IndexedStackのchildren内に期待した要素が存在するかどうかの確認をしたかった。通常の画面であればIndexedStackのchildren内にある要素のうち、指定したindex以外の要素は画面に描画されておらず、WidgetTestのFinderを使ったexpect(find.text('expected text', findsOneWidget);のようなテストはパスしない。

この記事では、画面に描画はされていないが存在は確認したいような場合のテストをどのように記述するかを残しておく。

テスト方法

結論としては、Finderのメソッドの引数にskipOffstage: falseを渡す。

expect(find.text('expected text', skipOffstage: false), findsOneWidget);

以下、深掘りしていく。

skipOffstageという引数について、Finderのソースコードには以下のようにコメントされている。

If the skipOffstage argument is true (the default), then this skips nodes that are [Offstage] or that are from inactive [Route]s.
ref: https://github.com/flutter/flutter/blob/d0482116e77994122a7e8596f4a6b7078f08e190/packages/flutter_test/lib/src/finders.dart#L80C1-L81C68

(拙訳) skipOffstage引数がtrueの場合(デフォルト)、これはOffstageであるノードや、非アクティブなRouteからのノードをスキップします。

Offstageを知らなかったので調べてみた。

A widget that lays the child out as if it was in the tree, but without painting anything, without making the child available for hit testing, and without taking any room in the parent.

Offstage children are still active: they can receive focus and have keyboard input directed to them.

Animations continue to run in offstage children, and therefore use battery and CPU time, regardless of whether the animations end up being visible.

Offstage can be used to measure the dimensions of a widget without bringing it on screen (yet). To hide a widget from view while it is not needed, prefer removing the widget from the tree entirely rather than keeping it alive in an Offstage subtree.

要するに画面上には描画されていないが出番待ちの状態にあるWidgetがOffstageに格納されており、テストの際はおそらくパフォーマンスの向上のために走査対象からスキップしているのだろう。

というわけでskipOffstage: falseというフラグを引数として渡すことで、Offstage内にある要素もテストで確認できるということのようだ。

ちなみにこの引数に気づく前は以下のようなテストを書いていた。対象のIndexedStackを見つけて直接childrenを見るやり方でこれはこれで明示的で悪くないが、記述量が多くて見通しが悪い。

final indexedStack = find.byType(IndexedStack);
expect(indexedStack, findsOneWidget);

final IndexedStack indexedStackWidget = tester.widget(indexedStack) as IndexedStack;
final children = indexedStackWidget.children;

expect(find.text('element1'), findsOneWidget);

bool hasElement2 = children.any((Widget widget) {
  return widget is Center && widget.child is Text && (widget.child as Text).data == 'element2';
});
expect(hasElement2, isTrue);

おわり

FlutterのWidgetはまだまだ知らないことだらけだ。


関連記事
最新記事
プロモーション

This site uses Google Analytics.