How to Test Navigation Via Navigator In Flutter ??

4 min read
How to Test Navigation Via Navigator In Flutter
How to Test Navigation Via Navigator In Flutter

Earlier we have been through various articles based on how to change from Stateless Widget to Stateful Widget. So, in this article, we will go through How to Test Navigation Via Navigator In Flutter.

Take a deep dive into Learning Flutter with Flutter Agency.

How to Test Navigation Via Navigator In Flutter??

In the navigator tests in the flutter repo they use the NavigatorObserver class to observe navigations:

igations:

class TestObserver extends NavigatorObserver {
  OnObservation onPushed;
  OnObservation onPopped;
  OnObservation onRemoved;
  OnObservation onReplaced;

  @override
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (onPushed != null) {
      onPushed(route, previousRoute);
    }
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (onPopped != null) {
      onPopped(route, previousRoute);
    }
  }

  @override
  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (onRemoved != null)
      onRemoved(route, previousRoute);
  }

  @override
  void didReplace({ Route<dynamic> oldRoute, Route<dynamic> newRoute }) {
    if (onReplaced != null)
      onReplaced(newRoute, oldRoute);
  }
}

Navigation could be abstracted away from a screen or a widget. The test can mock and inject this abstraction. This approach should be sufficient for testing such behavior.

There are several ways how to achieve that. I will show one of those, for purpose of this response. Perhaps it’s possible to simplify it a bit or to make it more “Darty”.

Abstraction for navigation 馃憞

class AppNavigatorFactory {
  AppNavigator get(BuildContext context) =>
      AppNavigator._forNavigator(Navigator.of(context));
}

class TestAppNavigatorFactory extends AppNavigatorFactory {
  final AppNavigator mockAppNavigator;

  TestAppNavigatorFactory(this.mockAppNavigator);

  @override
  AppNavigator get(BuildContext context) => mockAppNavigator;
}

class AppNavigator {
  NavigatorState _flutterNavigator;
  AppNavigator._forNavigator(this._flutterNavigator);

  void showNextscreen() {
    _flutterNavigator.pushNamed('/nextscreen');
  }
}

Injection into a widget 馃憞

class MyScreen extends StatefulWidget {
  final _appNavigatorFactory;
  MyScreen(this._appNavigatorFactory, {Key key}) : super(key: key);

  @override
  _MyScreenState createState() => _MyScreenState(_appNavigatorFactory);
}

class _MyScreenState extends State<MyScreen> {
  final _appNavigatorFactory;

  _MyScreenState(this._appNavigatorFactory);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: RaisedButton(
                onPressed: () {
                    _appNavigatorFactory.get(context).showNextscreen();
                },
                child: Text(Strings.traktTvUrl)
            )
        )
    );
  }

}

Example of a test (Uses聽Mockito for Dart) 馃憞

class MockAppNavigator extends Mock implements AppNavigator {}

void main() {
  final appNavigator = MockAppNavigator();

  setUp(() {
    reset(appNavigator);
  });


  testWidgets('Button is present and triggers navigation after tapped',
      (WidgetTester tester) async {

    await tester.pumpWidget(MaterialApp(home: MyScreen(TestAppNavigatorFactory())));

    expect(find.byType(RaisedButton), findsOneWidget);
    await tester.tap(find.byType(RaisedButton));

    verify(appNavigator.showNextscreen());
  });
}

So you can create a mocked NavigatorObserver to avoid any extra boilerplate:

import 'package:mockito/mockito.dart';

class MockNavigatorObserver extends Mock implements NavigatorObserver {}

So that would translate to your test case as shown in the below snippet:

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);
  });
}

You can find a detailed article on it from which you can find it聽here.

Conclusion:

So in this article, we have been through How to Test Navigation Via Navigator In Flutter.

If you need articles or videos on any specific topic, Do let us know.

Keep聽Learning聽!!! Keep聽Fluttering聽!!!

Do let us know in the comments if you are still confused in flutter!! we would love to help you 馃檪

FlutterAgency.com聽is our portal Platform dedicated to Flutter Technology and聽Flutter Developers. The portal is full of cool resources from Flutter like聽Flutter Widget聽Guide,聽Flutter Projects,聽Code libs聽and etc.

FlutterAgency.com聽is one of the most popular online portals dedicated to聽Flutter Technology聽and daily thousands of unique visitors come to this portal to enhance their knowledge of聽Flutter.

Leave a Reply