【Flutter】l10nを含んだメソッドをテストする
はじめに
以下のようなl10n(localization)が絡んだメソッドのユニットテストを書こうとした時に、context
をどう用意するかで少し悩んだので、私なりの解決方法を書こうと思います。
class TextGenerator {
static String getHogeText(BuildContext context) {
return AppLocalizations.of(context)!.hoge;
}
}
実装
結論から話すと以下のように実装しました。
l10nの設定を渡したMaterialApp()
内のBuildContext
を取得して使っています。
testWidgets("description", (tester) async {
await tester.pumpWidget(const MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Placeholder(),
));
await tester.pumpAndSettle();
final BuildContext context = tester.element(find.byType(Placeholder));
expect(
TextGenerator.getHogeText(context),
AppLocalizations.of(context)!.hoge,
);
});
解説
なぜ上記のように実装したかを説明します。
await tester.pumpWidget(const MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Placeholder(),
));
まず、実際にアプリ内で使っているl10nの設定を渡したMaterialApp()
に、適当なWidgetを渡しています。(Placeholder()
の部分はContainer()
でもなんでもいいです。)
ここで、localizationsDelegates:
とsupportedLocales:
を設定してあげないと、AppLocalizations.of(context)
がnull
になります。
(設定という表現が適切かは分かりませんが、この記事ではそう呼びます。)
なぜnullになるのか?
AppLocalizations.of()
のof()
の中を辿っていくと、以下のresourcesFor()
が呼ばれています。
_typeToResources
には今のcontext
に含まれているl10nの設定が格納されており、その中にT
が存在するかを確認しています。
AppLocalizations.of()
を呼んだ際、T
にはAppLocalizations?
が入るので、要するに今のcontext
にAppLocalizations
が設定されているかをチェックします。されていなければnull
が返ります。
await tester.pumpAndSettle();
次に、描画が終わるまで待ちます。
これが無いと、描画が終わる前にBuildContext
を取得しようとして失敗し、テストが落ちることがあります。
(l10nの量によっては無くても動きますが、あった方が安心です。)
final BuildContext context = tester.element(find.byType(Placeholder));
次に、使用したいl10nの設定が存在するwidget tree内からBuildContext
を取得します。
find.byType()
の引数には、MaterialApp()
のhome
に渡したWidgetの型を指定します。
elementって?
ここで呼んでいるtester.element()
が返すElement
ですが、
Element Tree
のElementです。
Element
はBuildContext
を実装(implements)しているので、BuildContext
として受け取れます。
普段使っているWidgetのbuild()
メソッドには、
StatelessElement
や、
StatefulElement
等が渡されています。
例えば、StatelessWidget
のbuild()
メソッドではStatelessElement
が渡されます。
expect(
TextGenerator.getHogeText(context),
AppLocalizations.of(context)!.hoge,
);
これでAppLocalizations
が使えるcontext
が取得出来たので、無事にテスト出来ます。
終わりに
ユニットテストでtestWidgets()
を使っていることに気持ち悪さはありますが、context
に依存する以上、こうするしか方法がないのかなぁと思います。
Discussion