【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