How to test navigation via Navigator in Flutter

后端 未结 3 1392
走了就别回头了
走了就别回头了 2020-12-16 13:00

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

3条回答
  •  夕颜
    夕颜 (楼主)
    2020-12-16 13:49

    Following solution is, let's say, a general approach and it's not specific to Flutter.

    Navigation could be abstracted away from a screen or a widget. 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 {
      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());
      });
    }
    

提交回复
热议问题