Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test navigation via Navigator in Flutter

Let's say, I have a test for a screen in Flutter using WidgetTester. There is a button, which executes a navigation via Navigator. I would like to test behavior of that button.

Widget/Screen

class MyScreen extends StatefulWidget {    MyScreen({Key key}) : super(key: key);    @override   _MyScreenState createState() => _MyScreenScreenState(); }  class _MyScreenState extends State<MyScreen> {    @override   Widget build(BuildContext context) {     return Scaffold(         body: Center(             child: RaisedButton(                 onPressed: () {                     Navigator.of(context).pushNamed("/nextscreen");                 },                 child: Text(Strings.traktTvUrl)             )         )     );   }  } 

Test

void main() {    testWidgets('Button is present and triggers navigation after tapped',       (WidgetTester tester) async {     await tester.pumpWidget(MaterialApp(home: MyScreen()));     expect(find.byType(RaisedButton), findsOneWidget);     await tester.tap(find.byType(RaisedButton));     //how to test navigator?       }); } 

I there a proper way how to check, that Navigator was called? Or is there a way to mock and replace navigator?

Pleas note, that code above will actually fail with an exception, because there is no named route '/nextscreen' declared in application. That's simple to solve and you don't need to point it out.

My main concern is how to correctly approach this test scenario in Flutter.

like image 647
Josef Adamcik Avatar asked Jun 05 '18 16:06

Josef Adamcik


People also ask

How do you navigate between pages in Flutter?

The basic way — push and pop The Navigator behaves like a stack, so the most simple way is to push a new page onto it when you want to navigate to it and to pop a page if you want to go back.

How do you get data from navigator in Flutter?

To return data to the first screen, use the Navigator. pop() method, which accepts an optional second argument called result .


1 Answers

While what Danny said is correct and works, you can also create a mocked NavigatorObserver to avoid any extra boilerplate:

import 'package:mockito/mockito.dart';  class MockNavigatorObserver extends Mock implements NavigatorObserver {} 

That would translate to your test case as follows:

void main() {   testWidgets('Button is present and triggers navigation after tapped',       (WidgetTester tester) async {     final mockObserver = MockNavigatorObserver();     await tester.pumpWidget(       MaterialApp(         home: MyScreen(),         navigatorObservers: [mockObserver],       ),     );      expect(find.byType(RaisedButton), findsOneWidget);     await tester.tap(find.byType(RaisedButton));     await tester.pumpAndSettle();      /// Verify that a push event happened     verify(mockObserver.didPush(any, any));      /// You'd also want to be sure that your page is now     /// present in the screen.     expect(find.byType(DetailsPage), findsOneWidget);   }); } 

I wrote an in-depth article about this on my blog, which you can find here.

like image 188
Iiro Krankka Avatar answered Sep 21 '22 13:09

Iiro Krankka