代码之家  ›  专栏  ›  技术社区  ›  ssiddh

如何在flatter小部件测试中等待将来完成?

  •  4
  • ssiddh  · 技术社区  · 7 年前

    我正在尝试执行一个小部件测试,特别是导航测试。我正在使用bloc架构,在bloc上设置一个流触发bloc内的一系列事件,从服务器调用获取会话信息(它返回会话信息对象的未来),在成功的服务器调用上设置一个登录流,小部件订阅了该流并导航到下一个屏幕。

    pumpAndSettle()

    如果我没有说清楚,我很抱歉,但下面是示例代码:

    登录_团块。飞镖

    class LoginBloc {
      LoginRepository _loginRepository;
      final String searchKeyword = "special-keyword";
    
      final _urlString = PublishSubject<String>();
      final _isLoggedIn = BehaviorSubject<bool>(seedValue: false);
      final _errorMessage = PublishSubject<String>();
    
      Observable<bool> get isLoggedIn => _isLoggedIn.stream;
      Observable<String> get isErrorState => _errorMessage.stream;
    
      LoginBloc({LoginRepository loginRepository})
          : _loginRepository = loginRepository ?? LoginRepository() {
              // Listen on the _urlString stream to call the function which checks for the special keyword and if a match is found make a server call
        _urlString.stream.listen((String url) {
          _authorizationFullService(url);
        });
      }
    
        // Search for special keyword and if a match is found call the server call function
      void _authorizationFullService(String url) {
        if (url.contains(searchKeyword)) {
          int index = url.indexOf(searchKeyword);
          String result = url.substring(index + searchKeyword.length);
          result = result.trim();
          String decodedUrl = Uri.decodeFull(result);
          if (decodedUrl != null && decodedUrl.length > 0) {
            _fullServiceServerCall(decodedUrl);
          } else {
            _isLoggedIn.sink.add(false);
          }
        }
      }
    
        // Call server call function from repository which returns a future of the Authorization object
      void _fullServiceServerCall(String decodedUrl) {
        _loginRepository
            .getSession(decodedUrl)
            .then(_handleSuccessAuthorization)
            .catchError(_handleErrorState);
      }
    
        // Handle success response and set the login stream
      void _handleSuccessAuthorization(Authorization authorization) {
        if (authorization != null && authorization.idnumber != 0) {
          _isLoggedIn.sink.add(true);
        } else {
          _isLoggedIn.sink.add(false);
        }
      }
    
        // Handle error response and set the error stream
      void _handleErrorState(dynamic error) {
        _isLoggedIn.sink.add(false);
        _errorMessage.sink.add(error.toString());
      }
    
      void dispose() {
        _urlString.close();
        _isLoggedIn.close();
        _errorMessage.close();
      }
    }
    

    group('Full Login Navigation test', () {
        LoginRepository mockLoginRepository;
        LoginBloc loginBloc;
        NotificationBloc notificationBloc;
        NavigatorObserver mockNavigatorObserver;
        Authorization _auth;
        String testUrl;
    
        setUp(() {
          mockLoginRepository = MockLoginRepository();
          _auth = Authorization((auth) => auth
            ..param1 = "foo"
            ..param2 = "bar"
            ..param3 = "foobar"
            ..param4 = "barfoo");
          loginBloc = LoginBloc(loginRepository: mockLoginRepository);
          mockNavigatorObserver = MockNavigatorObserver();
          testUrl = "http://test.test.com";
        });
    
        Future<Null> _buildFullLoginPage(LoginBloc loginBloc,
            NotificationBloc notificationBloc, WidgetTester tester) async {
          when(mockLoginRepository.getSession(testUrl))
              .thenAnswer((_) => Future.value(_auth));
          await tester.pumpWidget(LoginBlocProvider(
            child: NotificationBlocProvider(
              child: MaterialApp(
                home: LoginFullService(),
                onGenerateRoute: NavigationRoutes.routes,
                navigatorObservers: [mockNavigatorObserver],
              ),
              notificationBloc: notificationBloc,
            ),
            loginBloc: loginBloc,
          ));
          //TODO: Remove casting to dynamic after dart sdk bug fix: https://github.com/dart-lang/mockito/issues/163
          verify(mockNavigatorObserver.didPush(any, any) as dynamic);
          loginBloc.getAuthorization(
              "http://testing.testing.com?search-keyword=http%3A%2F%2Ftest.test.com");
        }
    
        testWidgets('Navigate to landing page on correct login url',
            (WidgetTester tester) async {
          await _buildFullLoginPage(loginBloc, notificationBloc, tester);
          await tester.pumpAndSettle();
          expect(find.byKey(Key('webview_scaffold')), findsNothing);
          //TODO: Remove casting to dynamic after dart sdk bug fix: https://github.com/dart-lang/mockito/issues/163
          verify(mockNavigatorObserver.didPush(any, any) as dynamic);
        });
    });
    

    在运行小部件时 tester.pumpAndSettle() 里面 testWidgets 在未来完成之前超时。这是错误日志:

    ══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
    The following assertion was thrown running a test:
    pumpAndSettle timed out
    
    When the exception was thrown, this was the stack:
    #0      WidgetTester.pumpAndSettle.<anonymous closure> (package:flutter_test/src/widget_tester.dart:299:11)
    <asynchronous suspension>
    #3      TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:69:41)
    #4      WidgetTester.pumpAndSettle (package:flutter_test/src/widget_tester.dart:295:27)
    #5      main.<anonymous closure>.<anonymous closure> (file:///Users/ssiddh/Documents/projects/mobile-flutter/test/ui/pages/login/login_full_test.dart:114:20)
    <asynchronous suspension>
    #6      testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:72:23)
    #7      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:555:19)
    <asynchronous suspension>
    #10     TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:539:14)
    #11     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:883:24)
    #17     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:880:15)
    #18     testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:71:22)
    #19     Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test/src/backend/declarer.dart:168:27)
    <asynchronous suspension>
    #20     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test/src/backend/invoker.dart:249:15)
    <asynchronous suspension>
    #25     Invoker.waitForOutstandingCallbacks (package:test/src/backend/invoker.dart:246:5)
    #26     Declarer.test.<anonymous closure>.<anonymous closure> (package:test/src/backend/declarer.dart:166:33)
    #31     Declarer.test.<anonymous closure> (package:test/src/backend/declarer.dart:165:13)
    <asynchronous suspension>
    #32     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test/src/backend/invoker.dart:403:25)
    <asynchronous suspension>
    #46     _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
    #47     _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
    #48     _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:169:12)
    (elided 30 frames from class _FakeAsync, package dart:async, and package stack_trace)
    

    我真的很感激任何形式的帮助或反馈。

    1 回复  |  直到 7 年前
        1
  •  19
  •   Günter Zöchbauer    7 年前

    试着用

    testWidgets('Navigate to landing page on correct login url',
        (WidgetTester tester) async {
        await tester.runAsync(() async {
    
          // test code here
    
        });
     });
    
    推荐文章