首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在颤动中通过Navigator测试导航

如何在颤动中通过Navigator测试导航
EN

Stack Overflow用户
提问于 2018-06-06 00:13:12
回答 4查看 20K关注 0票数 30

比方说,我使用WidgetTester在Flutter中测试了一个屏幕。有一个按钮,它通过Navigator执行导航。我想测试一下那个按钮的行为。

小工具/屏幕

代码语言:javascript
复制
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)
            )
        )
    );
  }

}

测试

代码语言:javascript
复制
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?    
  });
}

有没有合适的方法检查一下,那个Navigator是怎么调用的?或者有没有办法模拟和替换导航器?

请注意,上述代码实际上将失败并引发异常,因为应用程序中没有声明命名路由'/nextscreen'。这很容易解决,而且你不需要指出它。

我主要关心的是如何在Flutter中正确地处理这个测试场景。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-06-06 04:59:31

navigator tests in the flutter repo中,它们使用NavigatorObserver类来观察导航:

代码语言:javascript
复制
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);
  }
}

这看起来应该可以做你想做的事情,但是它可能只在顶层(MaterialApp)工作,我不确定你是否可以将它提供给一个小部件。

票数 9
EN

Stack Overflow用户

发布于 2018-08-23 18:01:48

虽然丹尼所说的是正确的和有效的,你也可以创建一个模拟的NavigatorObserver来避免任何额外的样板文件:

代码语言:javascript
复制
import 'package:mockito/mockito.dart';

class MockNavigatorObserver extends Mock implements NavigatorObserver {}

这将转换为您的测试用例,如下所示:

代码语言:javascript
复制
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);
  });
}

我在我的博客which you can find here上写了一篇关于这方面的深入文章。

票数 79
EN

Stack Overflow用户

发布于 2018-06-06 19:15:45

下面的解决方案,比方说,是一个通用的方法,它不是特定于颤动的。

导航可以从屏幕或小部件中抽象出来。测试可以模拟和注入这种抽象。这种方法应该足以测试这样的行为。

有几种方法可以实现这一点。为了这个响应,我将展示其中的一个。也许可以把它简化一点,或者让它更"Darty“。

用于导航的抽象

代码语言:javascript
复制
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');
  }
}

小部件中的注入

代码语言:javascript
复制
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)
            )
        )
    );
  }

}

测试的示例(使用Mockito for Dart)

代码语言:javascript
复制
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());
  });
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50704647

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档