ユニットテストとウィジェットテストを追加した際に発生したエラーとその解消を記載します。
ユニットテストの導入
testパッケージのサイトの指示に従い、下記のようにバージョンを指定してユニットテスト用のパッケージをpubspec.yamlに追加します。
dev_dependencies:
test: ^1.16.8
以下を実行すると下記のようなエラーが発生しました。
flutter pub get
結果
Because no versions of test match >1.16.8 <2.0.0 and test 1.16.8 depends on test_api 0.3.0, test ^1.16.8 requires test_api 0.3.0.
And because every version of flutter_test from sdk depends on test_api 0.2.19, test ^1.16.8 is incompatible with flutter_test from sdk.
So, because canton_tree depends on both flutter_test any from sdk and test ^1.16.8, version solving failed.
これは、dev_dependencies
にもともと含まれていた下記のウィジェットテスト用のパッケージが依存するtest_api
と、今回追加したtest
パッケージが依存するtest_api
のバージョンが異なるため、解決できない競合が発生しているということのようです。
解決策
競合が発生しないバージョンのtest
パッケージに変更すればよいのですが、下記のようにバージョンをしていしないことで自動的に解決してくれるということがわかったので、ここに下記のように記載します。
dev_dependencies:
flutter_test:
sdk: flutter
test:
以上でこちらのページで紹介されているユニットテストが実行可能になりました。
ウィジェットテストの導入
TitleScreenウィジェットが表示されることをテストするために、testフォルダにtitle_screen_widget_test.dartというファイルを作成し、以下のコードを書きます。
import 'package:canton_tree/views/title_screen.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Top Page Widget is displayed', (WidgetTester tester) async {
await tester.pumpWidget(TitleScreen());
expect(find.byType(TextButton), findsNWidgets(6));
});
}
ウィジェットテストを実行すると、以下のようなエラーが表示されます。
No MediaQuery widget ancestor found.
TopPage widgets require a MediaQuery widget ancestor.
The specific widget that could not find a MediaQuery ancestor was:
TitleScreen
The ownership chain for the affected widget is: "TitleScreen ← [root]"
No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of().
This can happen because you have not added a WidgetsApp, CupertinoApp, or MaterialApp widget (those
widgets introduce a MediaQuery), or it can happen if the context you use comes from a widget above
those widgets.
解決策
MediaQueryウィジェットを使うことが前提となっているウィジェットをテストする場合は、以下のように該当ウィジェットをMaterialAppウィジェットなどで囲う必要があります。
void main() {
testWidgets('Title Screen is displayed', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: TopPage(),
),
);
expect(find.byType(TextButton), findsNWidgets(6));
});
}
しかし、routeを使っていたり、providerやlocalizationなどを使用している場合は、Mockや設定をする必要があります。
Mockを使うために以下のようにmockitoパッケージを追加します。
dev_dependencies:
flutter_test:
sdk: flutter
test:
mockito:
routeを使って、ウィジェットを表示させる場合は、こちらで説明されているように、NavigationObserverのMockを作成することでテスト可能になります。
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
void main() {
testWidgets('Title Screen is displayed', (WidgetTester tester) async {
final mockObserver = MockNavigatorObserver();
await tester.pumpWidget(
MaterialApp(
home: TopPage(),
navigatorObservers: [mockObserver],
),
);
await tester.pumpAndSettle();
expect(find.byType(TextButton), findsNWidgets(6));
});
}
Providerが使われている場合は、必要なProviderでMaterialAppを括る必要がありますが、以上でウィジェットのテストが実行できるようになりました。
参考サイト
https://flutter.dev/docs/cookbook/testing/unit/introduction
https://flutter.dev/docs/cookbook/testing/widget/introduction
https://stackoverflow.com/questions/50704647/how-to-test-navigation-via-navigator-in-flutter